From a4a7797826ef033b0205e0f8e676c3474331ff24 Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sun, 23 Dec 2018 11:00:22 +0000 Subject: [PATCH 01/11] Split into immutable buffers and Effect/ST buffer types. --- bower.json | 4 +- src/Node/Buffer.js | 161 ++++++++------------------- src/Node/Buffer.purs | 119 +++++++++----------- src/Node/Buffer/Effect.js | 74 +++++++++++++ src/Node/Buffer/Effect.purs | 132 ++++++++++++++++++++++ src/Node/Buffer/Effect/Unsafe.purs | 14 +++ src/Node/Buffer/ST.js | 74 +++++++++++++ src/Node/Buffer/ST.purs | 142 ++++++++++++++++++++++++ src/Node/Buffer/ST/Unsafe.purs | 14 +++ src/Node/Buffer/Unsafe.js | 11 -- src/Node/Buffer/Unsafe.purs | 10 -- test/Main.purs | 137 ----------------------- test/Test/Main.purs | 13 +++ test/Test/Node/Buffer.purs | 129 ++++++++++++++++++++++ test/Test/Node/Buffer/Effect.purs | 161 +++++++++++++++++++++++++++ test/Test/Node/Buffer/ST.purs | 169 +++++++++++++++++++++++++++++ 16 files changed, 1022 insertions(+), 342 deletions(-) create mode 100644 src/Node/Buffer/Effect.js create mode 100644 src/Node/Buffer/Effect.purs create mode 100644 src/Node/Buffer/Effect/Unsafe.purs create mode 100644 src/Node/Buffer/ST.js create mode 100644 src/Node/Buffer/ST.purs create mode 100644 src/Node/Buffer/ST/Unsafe.purs delete mode 100644 src/Node/Buffer/Unsafe.js delete mode 100644 src/Node/Buffer/Unsafe.purs delete mode 100644 test/Main.purs create mode 100644 test/Test/Main.purs create mode 100644 test/Test/Node/Buffer.purs create mode 100644 test/Test/Node/Buffer/Effect.purs create mode 100644 test/Test/Node/Buffer/ST.purs diff --git a/bower.json b/bower.json index 229881b..3fb2aa1 100644 --- a/bower.json +++ b/bower.json @@ -15,7 +15,9 @@ "dependencies": { "purescript-effect": "^2.0.0", "purescript-maybe": "^4.0.0", - "purescript-arraybuffer-types": "^2.0.0" + "purescript-arraybuffer-types": "^2.0.0", + "purescript-st": "^4.0.0", + "purescript-unsafe-coerce": "^4.0.0" }, "devDependencies": { "purescript-assert": "^4.0.0", diff --git a/src/Node/Buffer.js b/src/Node/Buffer.js index e96639d..3809ae9 100644 --- a/src/Node/Buffer.js +++ b/src/Node/Buffer.js @@ -5,44 +5,53 @@ exports.showImpl = require('util').inspect; -exports.create = function (size) { - return function() { - return Buffer.alloc(size); +exports.eqImpl = function(a) { + return function(b) { + return a.equals(b); + } +}; + +exports.compareImpl = function(a) { + return function (b) { + return a.compare(b); }; +} + +exports.create = function (size) { + return Buffer.alloc(size); }; exports.fromArray = function (octets) { - return function() { - return Buffer.from(octets); - }; + return Buffer.from(octets); }; -exports.fromStringImpl = function (str) { - return function (encoding) { - return function() { - return Buffer.from(str, encoding); - }; - }; +exports.size = function (buff) { + return buff.length; }; -exports.fromArrayBuffer = function(ab) { - return function() { - return Buffer.from(ab); - }; +exports.toArray = function (buff) { + var json = buff.toJSON() + return json.data || json; }; exports.toArrayBuffer = function(buff) { - return function() { - return buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength); + return buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength); +}; + +exports.fromArrayBuffer = function(ab) { + return Buffer.from(ab); +}; + +exports.fromStringImpl = function (str) { + return function (encoding) { + return Buffer.from(str, encoding); }; }; exports.readImpl = function (ty) { return function (offset) { return function (buf) { - return function() { - return buf['read' + ty](offset); - }; + return buf['read' + ty](offset); }; }; }; @@ -51,126 +60,44 @@ exports.readStringImpl = function (enc) { return function (start) { return function (end) { return function (buff) { - return function() { - return buff.toString(enc, start, end); - }; + return buff.toString(enc, start, end); }; }; }; }; -exports.toStringImpl = function (enc) { - return function (buff) { - return function() { - return buff.toString(enc); - }; - }; -}; - -exports.writeImpl = function (ty) { - return function (value) { - return function (offset) { - return function (buf) { - return function() { - buf['write' + ty](value, offset); - return {}; - } - }; - }; - }; -}; - -exports.writeStringImpl = function (encoding) { - return function (offset) { - return function (length) { - return function (value) { - return function (buff) { - return function() { - return buff.write(value, offset, length, encoding); - } - }; - }; - }; - }; -}; - -exports.toArray = function (buff) { - return function() { - var json = buff.toJSON() - return json.data || json; - }; -}; - exports.getAtOffsetImpl = function (just) { return function (nothing) { return function (offset) { return function (buff) { - return function() { - var octet = buff[offset]; - return octet == null ? nothing - : just(octet); - }; + var octet = buff[offset]; + return octet == null ? nothing + : just(octet); }; }; }; }; -exports.setAtOffset = function (value) { - return function (offset) { - return function (buff) { - return function() { - buff[offset] = value; - return {}; - }; - }; +exports.toStringImpl = function (enc) { + return function (buff) { + return buff.toString(enc); }; }; -exports.size = function (buff) { - return function() { - return buff.length; +exports.slice = function (start) { + return function (end) { + return function (buff) { + return buff.slice(start, end); + }; }; }; - - exports.concat = function (buffs) { - return function() { - return Buffer.concat(buffs); - }; + return Buffer.concat(buffs); }; exports["concat'"] = function (buffs) { return function (totalLength) { - return function() { - return Buffer.concat(buffs, totalLength); - }; - }; -}; - -exports.copy = function (srcStart) { - return function (srcEnd) { - return function (src) { - return function (targStart) { - return function (targ) { - return function() { - return src.copy(targ, targStart, srcStart, srcEnd); - }; - }; - }; - }; - }; -}; - -exports.fill = function (octet) { - return function (start) { - return function (end) { - return function (buf) { - return function() { - buf.fill(octet, start, end); - return {}; - }; - }; - }; + return Buffer.concat(buffs, totalLength); }; }; diff --git a/src/Node/Buffer.purs b/src/Node/Buffer.purs index d90b9ab..81ab805 100644 --- a/src/Node/Buffer.purs +++ b/src/Node/Buffer.purs @@ -1,31 +1,27 @@ +-- | Immutable buffers and associated operations. module Node.Buffer - ( Octet() - , Offset() - , Buffer() + ( Octet + , Offset , BufferValueType(..) + , Buffer , create , fromArray , fromString , fromArrayBuffer - , toArrayBuffer , read , readString , toString - , write - , writeString , toArray + , toArrayBuffer , getAtOffset - , setAtOffset - , size , concat , concat' - , copy - , fill + , slice + , size ) where import Prelude -import Effect (Effect) import Data.ArrayBuffer.Types (ArrayBuffer) import Data.Maybe (Maybe(..)) import Node.Encoding (Encoding, encodingToNode) @@ -37,14 +33,6 @@ type Octet = Int -- | Type synonym indicating the value refers to an offset in a buffer. type Offset = Int --- | An instance of Node's Buffer class. -foreign import data Buffer :: Type - -instance showBuffer :: Show Buffer where - show = showImpl - -foreign import showImpl :: Buffer -> String - -- | Enumeration of the numeric types that can be written to a buffer. data BufferValueType = UInt8 @@ -78,87 +66,86 @@ instance showBufferValueType :: Show BufferValueType where show DoubleLE = "DoubleLE" show DoubleBE = "DoubleBE" +-- | An immutable buffer that exists independently of any memory region or effect. +foreign import data Buffer :: Type + +instance showBuffer :: Show Buffer where + show = showImpl + +foreign import showImpl :: Buffer -> String + +instance eqBuffer :: Eq Buffer where + eq = eqImpl + +foreign import eqImpl :: Buffer -> Buffer -> Boolean + +instance ordBuffer :: Ord Buffer where + compare a b = + case compareImpl a b of + x | x < 0 -> LT + x | x > 0 -> GT + otherwise -> EQ + +foreign import compareImpl :: Buffer -> Buffer -> Int + -- | Creates a new buffer of the specified size. -foreign import create :: Int -> Effect Buffer +foreign import create :: Int -> Buffer -- | Creates a new buffer from an array of octets, sized to match the array. -foreign import fromArray :: Array Octet -> Effect Buffer +foreign import fromArray :: Array Octet -> Buffer -- | Creates a buffer view from a JS ArrayByffer without copying data. -- -- Requires Node >= v5.10.0 -foreign import fromArrayBuffer :: ArrayBuffer -> Effect Buffer +foreign import fromArrayBuffer :: ArrayBuffer -> Buffer --- | Creates a new buffer from a string with the specified encoding, sized to --- | match the string. -fromString :: String -> Encoding -> Effect Buffer +-- | Creates a new buffer from a string with the specified encoding, sized to match the string. +fromString :: String -> Encoding -> Buffer fromString str = fromStringImpl str <<< encodingToNode -foreign import fromStringImpl :: String -> String -> Effect Buffer - -foreign import toArrayBuffer :: Buffer -> Effect ArrayBuffer +foreign import fromStringImpl :: String -> String -> Buffer -- | Reads a numeric value from a buffer at the specified offset. -read :: BufferValueType -> Offset -> Buffer -> Effect Int +read :: BufferValueType -> Offset -> Buffer -> Int read = readImpl <<< show -foreign import readImpl :: String -> Offset -> Buffer -> Effect Int +foreign import readImpl :: String -> Offset -> Buffer -> Int -- | Reads a section of a buffer as a string with the specified encoding. -readString :: Encoding -> Offset -> Offset -> Buffer -> Effect String +readString :: Encoding -> Offset -> Offset -> Buffer -> String readString = readStringImpl <<< encodingToNode foreign import readStringImpl :: - String -> Offset -> Offset -> Buffer -> Effect String + String -> Offset -> Offset -> Buffer -> String -- | Reads the buffer as a string with the specified encoding. -toString :: Encoding -> Buffer -> Effect String +toString :: Encoding -> Buffer -> String toString = toStringImpl <<< encodingToNode -foreign import toStringImpl :: String -> Buffer -> Effect String - --- | Writes a numeric value to a buffer at the specified offset. -write :: BufferValueType -> Int -> Offset -> Buffer -> Effect Unit -write = writeImpl <<< show - -foreign import writeImpl :: String -> Int -> Offset -> Buffer -> Effect Unit - --- | Writes octets from a string to a buffer at the specified offset. Multi-byte --- | characters will not be written to the buffer if there is not enough capacity --- | to write them fully. The number of bytes written is returned. -writeString :: Encoding -> Offset -> Int -> String -> Buffer -> Effect Int -writeString = writeStringImpl <<< encodingToNode - -foreign import writeStringImpl :: - String -> Offset -> Int -> String -> Buffer -> Effect Int +foreign import toStringImpl :: String -> Buffer -> String -- | Creates an array of octets from a buffer's contents. -foreign import toArray :: Buffer -> Effect (Array Octet) +foreign import toArray :: Buffer -> Array Octet + +-- | Creates an `ArrayBuffer` by copying a buffer's contents. +foreign import toArrayBuffer :: Buffer -> ArrayBuffer -- | Reads an octet from a buffer at the specified offset. -getAtOffset :: Offset -> Buffer -> Effect (Maybe Octet) +getAtOffset :: Offset -> Buffer -> Maybe Octet getAtOffset = getAtOffsetImpl Just Nothing foreign import getAtOffsetImpl :: - (Octet -> Maybe Octet) -> Maybe Octet -> Offset -> Buffer -> Effect (Maybe Octet) - --- | Writes an octet in the buffer at the specified offset. -foreign import setAtOffset :: Octet -> Offset -> Buffer -> Effect Unit - --- | Returns the size of a buffer. -foreign import size :: Buffer -> Effect Int + (Octet -> Maybe Octet) -> Maybe Octet -> Offset -> Buffer -> Maybe Octet -- | Concatenates a list of buffers. -foreign import concat :: Array Buffer -> Effect Buffer +foreign import concat :: Array Buffer -> Buffer -- | Concatenates a list of buffers, combining them into a new buffer of the -- | specified length. -foreign import concat' :: Array Buffer -> Int -> Effect Buffer +foreign import concat' :: Array Buffer -> Int -> Buffer --- | Copies a section of a source buffer into a target buffer at the specified --- | offset, and returns the number of octets copied. -foreign import copy :: Offset -> Offset -> Buffer -> Offset -> Buffer -> Effect Int +-- | Creates a new buffer slice that shares the memory of the original buffer. +foreign import slice :: Offset -> Offset -> Buffer -> Buffer --- | Fills a range in a buffer with the specified octet. -foreign import fill :: - Octet -> Offset -> Offset -> Buffer -> Effect Unit +-- | Returns the size of a buffer. +foreign import size :: Buffer -> Int diff --git a/src/Node/Buffer/Effect.js b/src/Node/Buffer/Effect.js new file mode 100644 index 0000000..845bfe1 --- /dev/null +++ b/src/Node/Buffer/Effect.js @@ -0,0 +1,74 @@ +/* global exports */ +/* global Buffer */ +"use strict"; + +exports.copyImpl = function(a) { + return function() { + return Buffer.from(a); + }; +}; + +exports.writeImpl = function (ty) { + return function (value) { + return function (offset) { + return function (buf) { + return function() { + buf['write' + ty](value, offset); + return {}; + } + }; + }; + }; +}; + +exports.writeStringImpl = function (encoding) { + return function (offset) { + return function (length) { + return function (value) { + return function (buff) { + return function() { + return buff.write(value, offset, length, encoding); + } + }; + }; + }; + }; +}; + +exports.setAtOffset = function (value) { + return function (offset) { + return function (buff) { + return function() { + buff[offset] = value; + return {}; + }; + }; + }; +}; + +exports.copy = function (srcStart) { + return function (srcEnd) { + return function (src) { + return function (targStart) { + return function (targ) { + return function() { + return src.copy(targ, targStart, srcStart, srcEnd); + }; + }; + }; + }; + }; +}; + +exports.fill = function (octet) { + return function (start) { + return function (end) { + return function (buf) { + return function() { + buf.fill(octet, start, end); + return {}; + }; + }; + }; + }; +}; diff --git a/src/Node/Buffer/Effect.purs b/src/Node/Buffer/Effect.purs new file mode 100644 index 0000000..a41eb2d --- /dev/null +++ b/src/Node/Buffer/Effect.purs @@ -0,0 +1,132 @@ +-- | Types and functions for working with mutable buffers using `Effect`. +module Node.Buffer.Effect + ( EffectBuffer + , create + , freeze + , thaw + , fromArray + , fromString + , fromArrayBuffer + , toArrayBuffer + , read + , readString + , toString + , write + , writeString + , toArray + , getAtOffset + , setAtOffset + , size + , concat + , concat' + , copy + , fill + ) where + +import Prelude + +import Data.ArrayBuffer.Types (ArrayBuffer) +import Data.Maybe (Maybe) +import Effect (Effect) +import Node.Buffer (Buffer, BufferValueType, Octet, Offset) +import Node.Buffer as Buffer +import Node.Encoding (Encoding, encodingToNode) +import Unsafe.Coerce (unsafeCoerce) + +-- | A reference to a mutable buffer, for use with `Effect` +foreign import data EffectBuffer :: Type + +usingFromFrozen :: forall a. (Buffer -> a) -> EffectBuffer -> Effect a +usingFromFrozen f buf = unsafeCoerce \_ -> f $ unsafeCoerce buf + +usingToFrozen :: forall a. (a -> Buffer) -> a -> Effect EffectBuffer +usingToFrozen f x = unsafeCoerce \_ -> unsafeCoerce $ f x + +-- | Creates a new buffer of the specified size. +create :: Int -> Effect EffectBuffer +create = usingToFrozen Buffer.create + +-- | Creates an immutable copy of a mutable buffer. +freeze :: EffectBuffer -> Effect Buffer +freeze = copyImpl + +-- | Creates a mutable copy of an immutable buffer. +thaw :: Buffer -> Effect EffectBuffer +thaw = copyImpl + +foreign import copyImpl :: forall a b. a -> Effect b + +-- | Creates a new buffer from an array of octets, sized to match the array. +fromArray :: Array Octet -> Effect EffectBuffer +fromArray = usingToFrozen Buffer.fromArray + +-- | Creates a new buffer from a string with the specified encoding, sized to +-- | match the string. +fromString :: String -> Encoding -> Effect EffectBuffer +fromString s = usingToFrozen $ Buffer.fromString s + +-- | Creates a buffer view from a JS ArrayByffer without copying data. +fromArrayBuffer :: ArrayBuffer -> Effect EffectBuffer +fromArrayBuffer = usingToFrozen Buffer.fromArrayBuffer + +-- | Copies the data in the buffer to a new JS ArrayBuffer +toArrayBuffer :: EffectBuffer -> Effect ArrayBuffer +toArrayBuffer = usingFromFrozen Buffer.toArrayBuffer + +-- | Reads a numeric value from a buffer at the specified offset. +read :: BufferValueType -> Offset -> EffectBuffer -> Effect Int +read t o = usingFromFrozen $ Buffer.read t o + +-- | Reads a section of a buffer as a string with the specified encoding. +readString :: Encoding -> Offset -> Offset -> EffectBuffer -> Effect String +readString e o o' = usingFromFrozen $ Buffer.readString e o o' + +-- | Reads the buffer as a string with the specified encoding. +toString :: Encoding -> EffectBuffer -> Effect String +toString e = usingFromFrozen $ Buffer.toString e + +-- | Writes a numeric value to a buffer at the specified offset. +write :: BufferValueType -> Int -> Offset -> EffectBuffer -> Effect Unit +write = writeImpl <<< show + +foreign import writeImpl :: String -> Int -> Offset -> EffectBuffer -> Effect Unit + +-- | Writes octets from a string to a buffer at the specified offset. Multi-byte +-- | characters will not be written to the buffer if there is not enough capacity +-- | to write them fully. The number of bytes written is returned. +writeString :: Encoding -> Offset -> Int -> String -> EffectBuffer -> Effect Int +writeString = writeStringImpl <<< encodingToNode + +foreign import writeStringImpl :: + String -> Offset -> Int -> String -> EffectBuffer -> Effect Int + +-- | Creates an array of octets from a buffer's contents. +toArray :: EffectBuffer -> Effect (Array Octet) +toArray = usingFromFrozen Buffer.toArray + +-- | Reads an octet from a buffer at the specified offset. +getAtOffset :: Offset -> EffectBuffer -> Effect (Maybe Octet) +getAtOffset o = usingFromFrozen $ Buffer.getAtOffset o + +-- | Writes an octet in the buffer at the specified offset. +foreign import setAtOffset :: Octet -> Offset -> EffectBuffer -> Effect Unit + +-- | Returns the size of a buffer. +size :: EffectBuffer -> Effect Int +size = usingFromFrozen Buffer.size + +-- | Concatenates a list of buffers. +concat :: Array EffectBuffer -> Effect EffectBuffer +concat arrs = unsafeCoerce \_ -> Buffer.concat (unsafeCoerce arrs) + +-- | Concatenates a list of buffers, combining them into a new buffer of the +-- | specified length. +concat' :: Array EffectBuffer -> Int -> Effect EffectBuffer +concat' arrs n = unsafeCoerce \_ -> Buffer.concat' (unsafeCoerce arrs) n + +-- | Copies a section of a source buffer into a target buffer at the specified +-- | offset, and returns the number of octets copied. +foreign import copy :: Offset -> Offset -> EffectBuffer -> Offset -> EffectBuffer -> Effect Int + +-- | Fills a range in a buffer with the specified octet. +foreign import fill :: Octet -> Offset -> Offset -> EffectBuffer -> Effect Unit diff --git a/src/Node/Buffer/Effect/Unsafe.purs b/src/Node/Buffer/Effect/Unsafe.purs new file mode 100644 index 0000000..f1d5160 --- /dev/null +++ b/src/Node/Buffer/Effect/Unsafe.purs @@ -0,0 +1,14 @@ +module Node.Buffer.Effect.Unsafe where + +import Node.Buffer as Buffer +import Node.Buffer (Offset) +import Node.Buffer.Effect (EffectBuffer) +import Unsafe.Coerce (unsafeCoerce) + +-- | Creates a new buffer slice that acts like a window on the original buffer. +-- | Writing to the slice buffer updates the original buffer and vice-versa. +-- +-- | This is considered unsafe as writing to a slice can result in action at a +-- | distance. +slice :: Offset -> Offset -> EffectBuffer -> EffectBuffer +slice = unsafeCoerce Buffer.slice diff --git a/src/Node/Buffer/ST.js b/src/Node/Buffer/ST.js new file mode 100644 index 0000000..845bfe1 --- /dev/null +++ b/src/Node/Buffer/ST.js @@ -0,0 +1,74 @@ +/* global exports */ +/* global Buffer */ +"use strict"; + +exports.copyImpl = function(a) { + return function() { + return Buffer.from(a); + }; +}; + +exports.writeImpl = function (ty) { + return function (value) { + return function (offset) { + return function (buf) { + return function() { + buf['write' + ty](value, offset); + return {}; + } + }; + }; + }; +}; + +exports.writeStringImpl = function (encoding) { + return function (offset) { + return function (length) { + return function (value) { + return function (buff) { + return function() { + return buff.write(value, offset, length, encoding); + } + }; + }; + }; + }; +}; + +exports.setAtOffset = function (value) { + return function (offset) { + return function (buff) { + return function() { + buff[offset] = value; + return {}; + }; + }; + }; +}; + +exports.copy = function (srcStart) { + return function (srcEnd) { + return function (src) { + return function (targStart) { + return function (targ) { + return function() { + return src.copy(targ, targStart, srcStart, srcEnd); + }; + }; + }; + }; + }; +}; + +exports.fill = function (octet) { + return function (start) { + return function (end) { + return function (buf) { + return function() { + buf.fill(octet, start, end); + return {}; + }; + }; + }; + }; +}; diff --git a/src/Node/Buffer/ST.purs b/src/Node/Buffer/ST.purs new file mode 100644 index 0000000..1232665 --- /dev/null +++ b/src/Node/Buffer/ST.purs @@ -0,0 +1,142 @@ +-- | Types and functions for working with mutable buffers using the `ST` effect. +-- | +-- | This module can be used when mutation is a local effect. +module Node.Buffer.ST + ( STBuffer + , run + , create + , freeze + , thaw + , fromArray + , fromString + , fromArrayBuffer + , toArrayBuffer + , read + , readString + , toString + , write + , writeString + , toArray + , getAtOffset + , setAtOffset + , size + , concat + , concat' + , copy + , fill + ) where + +import Prelude + +import Control.Monad.ST (ST, kind Region) +import Control.Monad.ST as ST +import Data.ArrayBuffer.Types (ArrayBuffer) +import Data.Maybe (Maybe) +import Node.Buffer (Buffer, BufferValueType, Octet, Offset) +import Node.Buffer as Buffer +import Node.Encoding (Encoding, encodingToNode) +import Unsafe.Coerce (unsafeCoerce) + +-- | A reference to a mutable buffer for use with `ST` +-- | +-- | The type parameter represents the memory region which the buffer belongs to. +foreign import data STBuffer :: Region -> Type + +usingFromFrozen :: forall a h. (Buffer -> a) -> STBuffer h -> ST h a +usingFromFrozen f buf = unsafeCoerce \_ -> f $ unsafeCoerce buf + +usingToFrozen :: forall a h. (a -> Buffer) -> a -> ST h (STBuffer h) +usingToFrozen f x = unsafeCoerce \_ -> unsafeCoerce $ f x + +-- | Runs an effect creating a mutable buffer, then freezes the buffer and returns it, without copying it. +run :: forall h. ST h (STBuffer h) -> Buffer +run st = ST.run (unsafeCoerce st) + +-- | Creates a new buffer of the specified size. +create :: forall h. Int -> ST h (STBuffer h) +create = usingToFrozen Buffer.create + +-- | Creates an immutable copy of a mutable buffer. +freeze :: forall h. STBuffer h -> ST h Buffer +freeze = copyImpl + +-- | Creates a mutable copy of an immutable buffer. +thaw :: forall h. Buffer -> ST h (STBuffer h) +thaw = copyImpl + +foreign import copyImpl :: forall h a b. a -> ST h b + +-- | Creates a new buffer from an array of octets, sized to match the array. +fromArray :: forall h. Array Octet -> ST h (STBuffer h) +fromArray = usingToFrozen Buffer.fromArray + +-- | Creates a new buffer from a string with the specified encoding, sized to +-- | match the string. +fromString :: forall h. String -> Encoding -> ST h (STBuffer h) +fromString s = usingToFrozen $ Buffer.fromString s + +-- | Creates a buffer view from a JS ArrayByffer without copying data. +fromArrayBuffer :: forall h. ArrayBuffer -> ST h (STBuffer h) +fromArrayBuffer = usingToFrozen Buffer.fromArrayBuffer + +-- | Copies the data in the buffer to a new JS ArrayBuffer +toArrayBuffer :: forall h. STBuffer h -> ST h ArrayBuffer +toArrayBuffer = usingFromFrozen Buffer.toArrayBuffer + +-- | Reads a numeric value from a buffer at the specified offset. +read :: forall h. BufferValueType -> Offset -> STBuffer h -> ST h Int +read t o = usingFromFrozen $ Buffer.read t o + +-- | Reads a section of a buffer as a string with the specified encoding. +readString :: forall h. Encoding -> Offset -> Offset -> STBuffer h -> ST h String +readString e o o' = usingFromFrozen $ Buffer.readString e o o' + +-- | Reads the buffer as a string with the specified encoding. +toString :: forall h. Encoding -> STBuffer h -> ST h String +toString e = usingFromFrozen $ Buffer.toString e + +-- | Writes a numeric value to a buffer at the specified offset. +write :: forall h. BufferValueType -> Int -> Offset -> STBuffer h -> ST h Unit +write = writeImpl <<< show + +foreign import writeImpl :: forall h. String -> Int -> Offset -> STBuffer h -> ST h Unit + +-- | Writes octets from a string to a buffer at the specified offset. Multi-byte +-- | characters will not be written to the buffer if there is not enough capacity +-- | to write them fully. The number of bytes written is returned. +writeString :: forall h. Encoding -> Offset -> Int -> String -> STBuffer h -> ST h Int +writeString = writeStringImpl <<< encodingToNode + +foreign import writeStringImpl :: + forall h. String -> Offset -> Int -> String -> STBuffer h -> ST h Int + +-- | Creates an array of octets from a buffer's contents. +toArray :: forall h. STBuffer h -> ST h (Array Octet) +toArray = usingFromFrozen Buffer.toArray + +-- | Reads an octet from a buffer at the specified offset. +getAtOffset :: forall h. Offset -> STBuffer h -> ST h (Maybe Octet) +getAtOffset o = usingFromFrozen $ Buffer.getAtOffset o + +-- | Writes an octet in the buffer at the specified offset. +foreign import setAtOffset :: forall h. Offset -> Offset -> STBuffer h -> ST h Unit + +-- | Returns the size of a buffer. +size :: forall h. STBuffer h -> ST h Int +size = usingFromFrozen Buffer.size + +-- | Concatenates a list of buffers. +concat :: forall h. Array (STBuffer h) -> ST h (STBuffer h) +concat arrs = unsafeCoerce \_ -> Buffer.concat (unsafeCoerce arrs) + +-- | Concatenates a list of buffers, combining them into a new buffer of the +-- | specified length. +concat' :: forall h. Array (STBuffer h) -> Int -> ST h (STBuffer h) +concat' arrs n = unsafeCoerce \_ -> Buffer.concat' (unsafeCoerce arrs) n + +-- | Copies a section of a source buffer into a target buffer at the specified +-- | offset, and returns the number of octets copied. +foreign import copy :: forall h. Offset -> Offset -> STBuffer h -> Offset -> STBuffer h -> ST h Int + +-- | Fills a range in a buffer with the specified octet. +foreign import fill :: forall h. Octet -> Offset -> Offset -> STBuffer h -> ST h Unit diff --git a/src/Node/Buffer/ST/Unsafe.purs b/src/Node/Buffer/ST/Unsafe.purs new file mode 100644 index 0000000..c11cf7f --- /dev/null +++ b/src/Node/Buffer/ST/Unsafe.purs @@ -0,0 +1,14 @@ +module Node.Buffer.ST.Unsafe where + +import Node.Buffer as Buffer +import Node.Buffer (Offset) +import Node.Buffer.ST (STBuffer) +import Unsafe.Coerce (unsafeCoerce) + +-- | Creates a new buffer slice that acts like a window on the original buffer. +-- | Writing to the slice buffer updates the original buffer and vice-versa. +-- +-- | This is considered unsafe as writing to a slice can result in action at a +-- | distance. +slice :: forall h. Offset -> Offset -> STBuffer h -> STBuffer h +slice = unsafeCoerce Buffer.slice diff --git a/src/Node/Buffer/Unsafe.js b/src/Node/Buffer/Unsafe.js deleted file mode 100644 index 139a70e..0000000 --- a/src/Node/Buffer/Unsafe.js +++ /dev/null @@ -1,11 +0,0 @@ -/* global exports */ -/* global Buffer */ -"use strict"; - -exports.slice = function (start) { - return function (end) { - return function (buff) { - return buff.slice(start, end); - }; - }; -}; diff --git a/src/Node/Buffer/Unsafe.purs b/src/Node/Buffer/Unsafe.purs deleted file mode 100644 index 930c214..0000000 --- a/src/Node/Buffer/Unsafe.purs +++ /dev/null @@ -1,10 +0,0 @@ -module Node.Buffer.Unsafe where - -import Node.Buffer - --- | Creates a new buffer slice that acts like a window on the original buffer. --- | Writing to the slice buffer updates the original buffer and vice-versa. --- --- | This is considered unsafe as writing to a slice can result in action at a --- | distance. -foreign import slice :: Offset -> Offset -> Buffer -> Buffer diff --git a/test/Main.purs b/test/Main.purs deleted file mode 100644 index cc4d548..0000000 --- a/test/Main.purs +++ /dev/null @@ -1,137 +0,0 @@ -module Test.Main where - -import Prelude -import Effect (Effect) -import Effect.Console (log) -import Data.Maybe (Maybe(..)) -import Data.Traversable (traverse) -import Node.Buffer (BufferValueType(..), toArray, concat', fromArray, fill, copy, readString, fromString, toString, read, write, create, getAtOffset) -import Node.Encoding (Encoding(..)) -import Test.Assert (assert') - -main :: Effect Unit -main = do - log "Testing..." - - log "Reading and writing" - testReadWrite - - log "fromArray" - testFromArray - - log "toArray" - testToArray - - log "fromString" - testFromString - - log "toString" - testToString - - log "readString" - testReadString - - log "copy" - testCopy - - log "fill" - testFill - - log "concat'" - testConcat' - - log "getAtOffset" - testGetAtOffset - -testReadWrite :: Effect Unit -testReadWrite = do - buf <- create 1 - let val = 42 - write UInt8 val 0 buf - readVal <- read UInt8 0 buf - - assertEq val readVal - -testFromArray :: Effect Unit -testFromArray = do - buf <- fromArray [1,2,3,4,5] - readVal <- read UInt8 2 buf - - assertEq 3 readVal - -testToArray :: Effect Unit -testToArray = do - let val = [1,2,67,3,3,7,8,3,4,237] - - buf <- fromArray val - valOut <- toArray buf - - assertEq val valOut - -testFromString :: Effect Unit -testFromString = do - let str = "hello, world" - - buf <- fromString str ASCII - val <- read UInt8 6 buf - - assertEq val 32 -- ASCII space - -testToString :: Effect Unit -testToString = do - let str = "hello, world" - - buf <- fromString str ASCII - strOut <- toString ASCII buf - - assertEq str strOut - -testReadString :: Effect Unit -testReadString = do - let str = "hello, world" - - buf <- fromString str ASCII - strOut <- readString ASCII 7 12 buf - - assertEq "world" strOut - -testCopy :: Effect Unit -testCopy = do - buf1 <- fromArray [1,2,3,4,5] - buf2 <- fromArray [10,9,8,7,6] - - copied <- copy 0 3 buf1 2 buf2 - out <- toArray buf2 - - assertEq copied 3 - assertEq out [10,9,1,2,3] - -testFill :: Effect Unit -testFill = do - buf <- fromArray [1,1,1,1,1] - fill 42 2 4 buf - out <- toArray buf - - assertEq [1,1,42,42,1] out - -testConcat' :: Effect Unit -testConcat' = do - bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] - buf <- concat' bufs 15 - out <- toArray buf - - assertEq [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14] out - -testGetAtOffset :: Effect Unit -testGetAtOffset = do - buf <- fromArray [1, 2, 3, 4] - assertEq (Just 2) =<< getAtOffset 1 buf - assertEq Nothing =<< getAtOffset 4 buf - assertEq Nothing =<< getAtOffset (-1) buf - -assertEq :: forall a. Eq a => Show a => a -> a -> Effect Unit -assertEq x y = - if x == y - then pure unit - else let msg = show x <> " and " <> show y <> " were not equal." - in assert' msg false diff --git a/test/Test/Main.purs b/test/Test/Main.purs new file mode 100644 index 0000000..9098e88 --- /dev/null +++ b/test/Test/Main.purs @@ -0,0 +1,13 @@ +module Test.Main where + +import Prelude +import Effect (Effect) +import Test.Node.Buffer as Buffer +import Test.Node.Buffer.Effect as Effect +import Test.Node.Buffer.ST as ST + +main :: Effect Unit +main = do + Buffer.test + Effect.test + ST.test diff --git a/test/Test/Node/Buffer.purs b/test/Test/Node/Buffer.purs new file mode 100644 index 0000000..bc98116 --- /dev/null +++ b/test/Test/Node/Buffer.purs @@ -0,0 +1,129 @@ +module Test.Node.Buffer (test) where + +import Prelude + +import Data.Maybe (Maybe(..)) +import Effect (Effect) +import Effect.Class.Console (log) +import Node.Buffer (Buffer, BufferValueType(..)) +import Node.Buffer as Buffer +import Node.Encoding (Encoding(..)) +import Test.Assert (assertEqual, assertTrue) + +test :: Effect Unit +test = do + log "Testing Node.Buffer ..." + + log " - show" + testShow + + log " - eq" + testEq + + log " - compare" + testCompare + + log " - create" + testCreate + + log " - fromString" + testFromString + + log " - toString" + testToString + + log " - toArray" + testToArray + + log " - readString" + testReadString + + log " - getAtOffset" + testGetAtOffset + + log " - (to/from)ArrayBuffer" + testToFromArrayBuffer + + log " - concat'" + testConcat' + + log " - slice" + testSlice + + log " - size" + testSize + +buffer123 :: Buffer +buffer123 = Buffer.fromArray [1, 2, 3] + +testShow :: Effect Unit +testShow = do + assertEqual {expected: "", actual: show buffer123} + +testEq :: Effect Unit +testEq = do + assertTrue $ buffer123 == buffer123 + assertTrue $ buffer123 == Buffer.fromArray [1, 2, 3] + assertTrue $ buffer123 /= Buffer.fromArray [1, 2, 4] + assertTrue $ buffer123 /= Buffer.fromArray [1, 2] + +testCompare :: Effect Unit +testCompare = do + assertEqual {expected: EQ, actual: compare buffer123 buffer123} + assertEqual {expected: LT, actual: compare buffer123 $ Buffer.fromArray [3, 2, 1]} + assertEqual {expected: GT, actual: compare buffer123 $ Buffer.fromArray [0, 1, 2]} + +testCreate :: Effect Unit +testCreate = do + assertEqual {expected: Buffer.fromArray [], actual: Buffer.create 0} + assertEqual {expected: Buffer.fromArray [0, 0, 0], actual: Buffer.create 3} + +testFromString :: Effect Unit +testFromString = do + let buf = Buffer.fromString "hello, world" ASCII + assertEqual {expected: 32, actual: Buffer.read UInt8 6 buf} + +testToString :: Effect Unit +testToString = do + let str = "hello, world" + str' = Buffer.toString ASCII $ Buffer.fromString str ASCII + assertEqual {expected: str, actual: str'} + +testToArray :: Effect Unit +testToArray = do + assertEqual {expected: [1, 2, 3], actual: Buffer.toArray buffer123} + +testReadString :: Effect Unit +testReadString = do + let str = "hello, world" + str' = Buffer.readString ASCII 7 12 $ Buffer.fromString str ASCII + assertEqual {expected: "world", actual: str'} + +testGetAtOffset :: Effect Unit +testGetAtOffset = do + assertEqual {expected: Just 2, actual: Buffer.getAtOffset 1 buffer123} + assertEqual {expected: Nothing, actual: Buffer.getAtOffset 99 buffer123} + assertEqual {expected: Nothing, actual: Buffer.getAtOffset (-1) buffer123} + +testToFromArrayBuffer :: Effect Unit +testToFromArrayBuffer = do + assertEqual {expected: buffer123, actual: Buffer.fromArrayBuffer $ Buffer.toArrayBuffer buffer123} + +testConcat' :: Effect Unit +testConcat' = do + let bufs = map (\x -> Buffer.fromArray [x, x+1, x+2]) [0,3,6,9,12] + buf = Buffer.concat' bufs 15 + out = Buffer.toArray buf + + assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} + +testSlice :: Effect Unit +testSlice = do + assertEqual {expected: buffer123, actual: Buffer.slice 0 3 buffer123} + assertEqual {expected: buffer123, actual: Buffer.slice 0 4 buffer123} + assertEqual {expected: Buffer.fromArray [2], actual: Buffer.slice 1 2 buffer123} + +testSize :: Effect Unit +testSize = do + assertEqual {expected: 0, actual: Buffer.size $ Buffer.fromArray []} + assertEqual {expected: 3, actual: Buffer.size buffer123} diff --git a/test/Test/Node/Buffer/Effect.purs b/test/Test/Node/Buffer/Effect.purs new file mode 100644 index 0000000..a489e06 --- /dev/null +++ b/test/Test/Node/Buffer/Effect.purs @@ -0,0 +1,161 @@ +module Test.Node.Buffer.Effect (test) where + +import Prelude + +import Data.Maybe (Maybe(..)) +import Data.Traversable (traverse) +import Effect (Effect) +import Effect.Console (log) +import Node.Buffer as Buffer +import Node.Buffer (BufferValueType(..)) +import Node.Buffer.Effect (toArray, concat', fromArray, fill, copy, readString, fromString, toString, freeze, thaw, read, write, create, getAtOffset) +import Node.Encoding (Encoding(..)) +import Test.Assert (assertEqual) + +test :: Effect Unit +test = do + log "Testing Node.Buffer.Effect ..." + + log " - create" + testCreate + + log " - freeze" + testFreeze + + log " - thaw" + testThaw + + log " - Reading and writing" + testReadWrite + + log " - fromArray" + testFromArray + + log " - toArray" + testToArray + + log " - fromString" + testFromString + + log " - toString" + testToString + + log " - readString" + testReadString + + log " - copy" + testCopy + + log " - fill" + testFill + + log " - concat'" + testConcat' + + log " - getAtOffset" + testGetAtOffset + +testCreate :: Effect Unit +testCreate = do + buf <- create 3 >>= toArray + assertEqual {expected: [0, 0, 0], actual: buf} + +testFreeze :: Effect Unit +testFreeze = do + buf <- fromArray [1, 2, 3] >>= freeze + assertEqual {expected: [1, 2, 3], actual: Buffer.toArray buf} + +testThaw :: Effect Unit +testThaw = do + buf <- (thaw $ Buffer.fromArray [1, 2, 3]) >>= toArray + assertEqual {expected: [1, 2, 3], actual: buf} + +testReadWrite :: Effect Unit +testReadWrite = do + buf <- create 1 + let val = 42 + write UInt8 val 0 buf + readVal <- read UInt8 0 buf + + assertEqual {expected: val, actual: readVal} + +testFromArray :: Effect Unit +testFromArray = do + buf <- fromArray [1,2,3,4,5] + readVal <- read UInt8 2 buf + + assertEqual {expected: 3, actual: readVal} + +testToArray :: Effect Unit +testToArray = do + let val = [1,2,67,3,3,7,8,3,4,237] + + buf <- fromArray val + valOut <- toArray buf + + assertEqual {expected: val, actual: valOut} + +testFromString :: Effect Unit +testFromString = do + let str = "hello, world" + + buf <- fromString str ASCII + val <- read UInt8 6 buf + + assertEqual {expected: 32, actual: val} -- ASCII space + +testToString :: Effect Unit +testToString = do + let str = "hello, world" + + buf <- fromString str ASCII + strOut <- toString ASCII buf + + assertEqual {expected: str, actual: strOut} + +testReadString :: Effect Unit +testReadString = do + let str = "hello, world" + + buf <- fromString str ASCII + strOut <- readString ASCII 7 12 buf + + assertEqual {expected: "world", actual: strOut} + +testCopy :: Effect Unit +testCopy = do + buf1 <- fromArray [1,2,3,4,5] + buf2 <- fromArray [10,9,8,7,6] + + copied <- copy 0 3 buf1 2 buf2 + out <- toArray buf2 + + assertEqual {expected: 3, actual: copied} + assertEqual {expected: [10,9,1,2,3], actual: out} + +testFill :: Effect Unit +testFill = do + buf <- fromArray [1,1,1,1,1] + fill 42 2 4 buf + out <- toArray buf + + assertEqual {expected: [1,1,42,42,1], actual: out} + +testConcat' :: Effect Unit +testConcat' = do + bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] + buf <- concat' bufs 15 + out <- toArray buf + + assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} + +testGetAtOffset :: Effect Unit +testGetAtOffset = do + buf <- fromArray [1, 2, 3, 4] + o1 <- getAtOffset 1 buf + o4 <- getAtOffset 4 buf + om1 <- getAtOffset (-1) buf + + assertEqual {expected: Just 2, actual: o1} + assertEqual {expected: Nothing, actual: o4} + assertEqual {expected: Nothing, actual: om1} diff --git a/test/Test/Node/Buffer/ST.purs b/test/Test/Node/Buffer/ST.purs new file mode 100644 index 0000000..44edd06 --- /dev/null +++ b/test/Test/Node/Buffer/ST.purs @@ -0,0 +1,169 @@ +module Test.Node.Buffer.ST (test) where + +import Prelude + +import Control.Monad.ST as ST +import Data.Maybe (Maybe(..)) +import Data.Traversable (traverse) +import Effect (Effect) +import Effect.Console (log) +import Node.Buffer as Buffer +import Node.Buffer (BufferValueType(..)) +import Node.Buffer.ST (freeze, thaw, concat', copy, create, fill, fromArray, fromString, getAtOffset, read, readString, run, toArray, toString, write) +import Node.Encoding (Encoding(..)) +import Test.Assert (assertEqual) + +test :: Effect Unit +test = do + log "Testing Node.Buffer.ST ..." + + log " - create" + testCreate + + log " - freeze" + testFreeze + + log " - thaw" + testThaw + + log " - Reading and writing" + testReadWrite + + log " - fromArray" + testFromArray + + log " - toArray" + testToArray + + log " - fromString" + testFromString + + log " - toString" + testToString + + log " - readString" + testReadString + + log " - copy" + testCopy + + log " - fill" + testFill + + log " - concat'" + testConcat' + + log " - getAtOffset" + testGetAtOffset + +testCreate :: Effect Unit +testCreate = do + let buf = run (create 3) + assertEqual {expected: [0, 0, 0], actual: Buffer.toArray buf} + +testFreeze :: Effect Unit +testFreeze = do + let buf = ST.run (fromArray [1, 2, 3] >>= freeze) + assertEqual {expected: [1, 2, 3], actual: Buffer.toArray buf} + +testThaw :: Effect Unit +testThaw = do + let buf = run (thaw $ Buffer.fromArray [1, 2, 3]) + assertEqual {expected: [1, 2, 3], actual: Buffer.toArray buf} + +testReadWrite :: Effect Unit +testReadWrite = do + let val = 42 + readVal = ST.run do + buf <- create 1 + write UInt8 val 0 buf + read UInt8 0 buf + + assertEqual {expected: val, actual: readVal} + +testFromArray :: Effect Unit +testFromArray = do + let readVal = ST.run do + buf <- fromArray [1,2,3,4,5] + read UInt8 2 buf + + assertEqual {expected: 3, actual: readVal} + +testToArray :: Effect Unit +testToArray = do + let val = [1,2,67,3,3,7,8,3,4,237] + valOut = ST.run do + buf <- fromArray val + toArray buf + + assertEqual {expected: val, actual: valOut} + +testFromString :: Effect Unit +testFromString = do + let str = "hello, world" + val = ST.run do + buf <- fromString str ASCII + read UInt8 6 buf + + assertEqual {expected: 32, actual: val} -- ASCII space + +testToString :: Effect Unit +testToString = do + let str = "hello, world" + strOut = ST.run do + buf <- fromString str ASCII + toString ASCII buf + + assertEqual {expected: str, actual: strOut} + +testReadString :: Effect Unit +testReadString = do + let str = "hello, world" + strOut = ST.run do + buf <- fromString str ASCII + readString ASCII 7 12 buf + + assertEqual {expected: "world", actual: strOut} + +testCopy :: Effect Unit +testCopy = do + let {copied, out} = ST.run do + buf1 <- fromArray [1,2,3,4,5] + buf2 <- fromArray [10,9,8,7,6] + copied <- copy 0 3 buf1 2 buf2 + out <- toArray buf2 + pure {copied, out} + + assertEqual {expected: 3, actual: copied} + assertEqual {expected: [10,9,1,2,3], actual: out} + +testFill :: Effect Unit +testFill = do + let out = ST.run do + buf <- fromArray [1,1,1,1,1] + fill 42 2 4 buf + toArray buf + + assertEqual {expected: [1,1,42,42,1], actual: out} + +testConcat' :: Effect Unit +testConcat' = do + let out = ST.run do + bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] + buf <- concat' bufs 15 + toArray buf + + assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} + +testGetAtOffset :: Effect Unit +testGetAtOffset = do + let {o1, o4, om1} = ST.run do + buf <- fromArray [1, 2, 3, 4] + o1 <- getAtOffset 1 buf + o4 <- getAtOffset 4 buf + om1 <- getAtOffset (-1) buf + pure {o1, o4, om1} + + assertEqual {expected: Just 2, actual: o1} + assertEqual {expected: Nothing, actual: o4} + assertEqual {expected: Nothing, actual: om1} From efcd6a4686b2669fffbf04a658ec741491adaa3d Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Fri, 28 Dec 2018 10:16:05 +0000 Subject: [PATCH 02/11] Move mutable buffer functions into a typeclass. There are two instances of the typeclass supplied: one for Effect + EffectBuffer and one for ST + STBuffer. This makes it possible to write code that mutates buffers that can run in either Effect or ST. The functional dependencies on MutableBuffer go in both directions so that the type-checker only needs to know _either_ the type of the buffer or the type of the effect: this should make it easier to write generic code without type assertions, but it does mean that both typeclass instances must go in the same module (Node.Buffer.Mutable) in order to avoid orphaned instances. --- bower.json | 3 +- src/Node/Buffer/Effect.purs | 132 ------------- src/Node/Buffer/Effect/Unsafe.purs | 14 -- src/Node/Buffer/{Effect.js => Mutable.js} | 12 +- src/Node/Buffer/Mutable.purs | 228 ++++++++++++++++++++++ src/Node/Buffer/Mutable/Unsafe.purs | 30 +++ src/Node/Buffer/ST.js | 74 ------- src/Node/Buffer/ST.purs | 142 -------------- src/Node/Buffer/ST/Unsafe.purs | 14 -- test/Test/Main.purs | 6 +- test/Test/Node/Buffer/Effect.purs | 161 --------------- test/Test/Node/Buffer/Mutable.purs | 203 +++++++++++++++++++ test/Test/Node/Buffer/Mutable/Unsafe.purs | 41 ++++ test/Test/Node/Buffer/ST.purs | 169 ---------------- 14 files changed, 512 insertions(+), 717 deletions(-) delete mode 100644 src/Node/Buffer/Effect.purs delete mode 100644 src/Node/Buffer/Effect/Unsafe.purs rename src/Node/Buffer/{Effect.js => Mutable.js} (83%) create mode 100644 src/Node/Buffer/Mutable.purs create mode 100644 src/Node/Buffer/Mutable/Unsafe.purs delete mode 100644 src/Node/Buffer/ST.js delete mode 100644 src/Node/Buffer/ST.purs delete mode 100644 src/Node/Buffer/ST/Unsafe.purs delete mode 100644 test/Test/Node/Buffer/Effect.purs create mode 100644 test/Test/Node/Buffer/Mutable.purs create mode 100644 test/Test/Node/Buffer/Mutable/Unsafe.purs delete mode 100644 test/Test/Node/Buffer/ST.purs diff --git a/bower.json b/bower.json index 3fb2aa1..962110e 100644 --- a/bower.json +++ b/bower.json @@ -22,6 +22,7 @@ "devDependencies": { "purescript-assert": "^4.0.0", "purescript-console": "^4.1.0", - "purescript-foldable-traversable": "^4.0.0" + "purescript-foldable-traversable": "^4.0.0", + "purescript-proxy": "^3.0.0" } } diff --git a/src/Node/Buffer/Effect.purs b/src/Node/Buffer/Effect.purs deleted file mode 100644 index a41eb2d..0000000 --- a/src/Node/Buffer/Effect.purs +++ /dev/null @@ -1,132 +0,0 @@ --- | Types and functions for working with mutable buffers using `Effect`. -module Node.Buffer.Effect - ( EffectBuffer - , create - , freeze - , thaw - , fromArray - , fromString - , fromArrayBuffer - , toArrayBuffer - , read - , readString - , toString - , write - , writeString - , toArray - , getAtOffset - , setAtOffset - , size - , concat - , concat' - , copy - , fill - ) where - -import Prelude - -import Data.ArrayBuffer.Types (ArrayBuffer) -import Data.Maybe (Maybe) -import Effect (Effect) -import Node.Buffer (Buffer, BufferValueType, Octet, Offset) -import Node.Buffer as Buffer -import Node.Encoding (Encoding, encodingToNode) -import Unsafe.Coerce (unsafeCoerce) - --- | A reference to a mutable buffer, for use with `Effect` -foreign import data EffectBuffer :: Type - -usingFromFrozen :: forall a. (Buffer -> a) -> EffectBuffer -> Effect a -usingFromFrozen f buf = unsafeCoerce \_ -> f $ unsafeCoerce buf - -usingToFrozen :: forall a. (a -> Buffer) -> a -> Effect EffectBuffer -usingToFrozen f x = unsafeCoerce \_ -> unsafeCoerce $ f x - --- | Creates a new buffer of the specified size. -create :: Int -> Effect EffectBuffer -create = usingToFrozen Buffer.create - --- | Creates an immutable copy of a mutable buffer. -freeze :: EffectBuffer -> Effect Buffer -freeze = copyImpl - --- | Creates a mutable copy of an immutable buffer. -thaw :: Buffer -> Effect EffectBuffer -thaw = copyImpl - -foreign import copyImpl :: forall a b. a -> Effect b - --- | Creates a new buffer from an array of octets, sized to match the array. -fromArray :: Array Octet -> Effect EffectBuffer -fromArray = usingToFrozen Buffer.fromArray - --- | Creates a new buffer from a string with the specified encoding, sized to --- | match the string. -fromString :: String -> Encoding -> Effect EffectBuffer -fromString s = usingToFrozen $ Buffer.fromString s - --- | Creates a buffer view from a JS ArrayByffer without copying data. -fromArrayBuffer :: ArrayBuffer -> Effect EffectBuffer -fromArrayBuffer = usingToFrozen Buffer.fromArrayBuffer - --- | Copies the data in the buffer to a new JS ArrayBuffer -toArrayBuffer :: EffectBuffer -> Effect ArrayBuffer -toArrayBuffer = usingFromFrozen Buffer.toArrayBuffer - --- | Reads a numeric value from a buffer at the specified offset. -read :: BufferValueType -> Offset -> EffectBuffer -> Effect Int -read t o = usingFromFrozen $ Buffer.read t o - --- | Reads a section of a buffer as a string with the specified encoding. -readString :: Encoding -> Offset -> Offset -> EffectBuffer -> Effect String -readString e o o' = usingFromFrozen $ Buffer.readString e o o' - --- | Reads the buffer as a string with the specified encoding. -toString :: Encoding -> EffectBuffer -> Effect String -toString e = usingFromFrozen $ Buffer.toString e - --- | Writes a numeric value to a buffer at the specified offset. -write :: BufferValueType -> Int -> Offset -> EffectBuffer -> Effect Unit -write = writeImpl <<< show - -foreign import writeImpl :: String -> Int -> Offset -> EffectBuffer -> Effect Unit - --- | Writes octets from a string to a buffer at the specified offset. Multi-byte --- | characters will not be written to the buffer if there is not enough capacity --- | to write them fully. The number of bytes written is returned. -writeString :: Encoding -> Offset -> Int -> String -> EffectBuffer -> Effect Int -writeString = writeStringImpl <<< encodingToNode - -foreign import writeStringImpl :: - String -> Offset -> Int -> String -> EffectBuffer -> Effect Int - --- | Creates an array of octets from a buffer's contents. -toArray :: EffectBuffer -> Effect (Array Octet) -toArray = usingFromFrozen Buffer.toArray - --- | Reads an octet from a buffer at the specified offset. -getAtOffset :: Offset -> EffectBuffer -> Effect (Maybe Octet) -getAtOffset o = usingFromFrozen $ Buffer.getAtOffset o - --- | Writes an octet in the buffer at the specified offset. -foreign import setAtOffset :: Octet -> Offset -> EffectBuffer -> Effect Unit - --- | Returns the size of a buffer. -size :: EffectBuffer -> Effect Int -size = usingFromFrozen Buffer.size - --- | Concatenates a list of buffers. -concat :: Array EffectBuffer -> Effect EffectBuffer -concat arrs = unsafeCoerce \_ -> Buffer.concat (unsafeCoerce arrs) - --- | Concatenates a list of buffers, combining them into a new buffer of the --- | specified length. -concat' :: Array EffectBuffer -> Int -> Effect EffectBuffer -concat' arrs n = unsafeCoerce \_ -> Buffer.concat' (unsafeCoerce arrs) n - --- | Copies a section of a source buffer into a target buffer at the specified --- | offset, and returns the number of octets copied. -foreign import copy :: Offset -> Offset -> EffectBuffer -> Offset -> EffectBuffer -> Effect Int - --- | Fills a range in a buffer with the specified octet. -foreign import fill :: Octet -> Offset -> Offset -> EffectBuffer -> Effect Unit diff --git a/src/Node/Buffer/Effect/Unsafe.purs b/src/Node/Buffer/Effect/Unsafe.purs deleted file mode 100644 index f1d5160..0000000 --- a/src/Node/Buffer/Effect/Unsafe.purs +++ /dev/null @@ -1,14 +0,0 @@ -module Node.Buffer.Effect.Unsafe where - -import Node.Buffer as Buffer -import Node.Buffer (Offset) -import Node.Buffer.Effect (EffectBuffer) -import Unsafe.Coerce (unsafeCoerce) - --- | Creates a new buffer slice that acts like a window on the original buffer. --- | Writing to the slice buffer updates the original buffer and vice-versa. --- --- | This is considered unsafe as writing to a slice can result in action at a --- | distance. -slice :: Offset -> Offset -> EffectBuffer -> EffectBuffer -slice = unsafeCoerce Buffer.slice diff --git a/src/Node/Buffer/Effect.js b/src/Node/Buffer/Mutable.js similarity index 83% rename from src/Node/Buffer/Effect.js rename to src/Node/Buffer/Mutable.js index 845bfe1..95b7a5d 100644 --- a/src/Node/Buffer/Effect.js +++ b/src/Node/Buffer/Mutable.js @@ -2,13 +2,13 @@ /* global Buffer */ "use strict"; -exports.copyImpl = function(a) { +exports.copyAllImpl = function(a) { return function() { return Buffer.from(a); }; }; -exports.writeImpl = function (ty) { +exports.writeInternal = function (ty) { return function (value) { return function (offset) { return function (buf) { @@ -21,7 +21,7 @@ exports.writeImpl = function (ty) { }; }; -exports.writeStringImpl = function (encoding) { +exports.writeStringInternal = function (encoding) { return function (offset) { return function (length) { return function (value) { @@ -35,7 +35,7 @@ exports.writeStringImpl = function (encoding) { }; }; -exports.setAtOffset = function (value) { +exports.setAtOffsetImpl = function (value) { return function (offset) { return function (buff) { return function() { @@ -46,7 +46,7 @@ exports.setAtOffset = function (value) { }; }; -exports.copy = function (srcStart) { +exports.copyImpl = function (srcStart) { return function (srcEnd) { return function (src) { return function (targStart) { @@ -60,7 +60,7 @@ exports.copy = function (srcStart) { }; }; -exports.fill = function (octet) { +exports.fillImpl = function (octet) { return function (start) { return function (end) { return function (buf) { diff --git a/src/Node/Buffer/Mutable.purs b/src/Node/Buffer/Mutable.purs new file mode 100644 index 0000000..db4a487 --- /dev/null +++ b/src/Node/Buffer/Mutable.purs @@ -0,0 +1,228 @@ +module Node.Buffer.Mutable + ( class MutableBuffer + , create + , freeze + , thaw + , fromArray + , fromString + , fromArrayBuffer + , toArrayBuffer + , read + , readString + , toString + , write + , writeString + , toArray + , getAtOffset + , setAtOffset + , size + , concat + , concat' + , copy + , fill + , EffectBuffer + , STBuffer + , runST + ) where + +import Prelude + +import Control.Monad.ST (ST, kind Region) +import Control.Monad.ST as ST +import Data.ArrayBuffer.Types (ArrayBuffer) +import Data.Maybe (Maybe) +import Effect (Effect) +import Node.Buffer (Buffer, BufferValueType, Octet, Offset) +import Node.Buffer as Buffer +import Node.Encoding (Encoding, encodingToNode) +import Unsafe.Coerce (unsafeCoerce) + +-- | A type class for mutable buffers `b` where operations on those buffers are +-- | represented by a particular effect type `e`. +class MutableBuffer b e | e -> b, b -> e where + + -- | Creates a new buffer of the specified size. + create :: Int -> e b + + -- | Creates an immutable copy of a mutable buffer. + freeze :: b -> e Buffer + + -- | Creates a mutable copy of an immutable buffer. + thaw :: Buffer -> e b + + -- | Creates a new buffer from an array of octets, sized to match the array. + fromArray :: Array Octet -> e b + + -- | Creates a new buffer from a string with the specified encoding, sized to + -- | match the string. + fromString :: String -> Encoding -> e b + + -- | Creates a buffer view from a JS ArrayByffer without copying data. + fromArrayBuffer :: ArrayBuffer -> e b + + -- | Copies the data in the buffer to a new JS ArrayBuffer + toArrayBuffer :: b -> e ArrayBuffer + + -- | Reads a numeric value from a buffer at the specified offset. + read :: BufferValueType -> Offset -> b -> e Int + + -- | Reads a section of a buffer as a string with the specified encoding. + readString :: Encoding -> Offset -> Offset -> b -> e String + + -- | Reads the buffer as a string with the specified encoding. + toString :: Encoding -> b -> e String + + -- | Writes a numeric value to a buffer at the specified offset. + write :: BufferValueType -> Int -> Offset -> b -> e Unit + + -- | Writes octets from a string to a buffer at the specified offset. Multi-byte + -- | characters will not be written to the buffer if there is not enough capacity + -- | to write them fully. The number of bytes written is returned. + writeString :: Encoding -> Offset -> Int -> String -> b -> e Int + + -- | Creates an array of octets from a buffer's contents. + toArray :: b -> e (Array Octet) + + -- | Reads an octet from a buffer at the specified offset. + getAtOffset :: Offset -> b -> e (Maybe Octet) + + -- | Writes an octet in the buffer at the specified offset. + setAtOffset :: Octet -> Offset -> b -> e Unit + + -- | Returns the size of a buffer. + size :: b -> e Int + + -- | Concatenates a list of buffers. + concat :: Array b -> e b + + -- | Concatenates a list of buffers, combining them into a new buffer of the + -- | specified length. + concat' :: Array b -> Int -> e b + + -- | Copies a section of a source buffer into a target buffer at the specified + -- | offset, and returns the number of octets copied. + copy :: Offset -> Offset -> b -> Offset -> b -> e Int + + -- | Fills a range in a buffer with the specified octet. + fill :: Octet -> Offset -> Offset -> b -> e Unit + +-- | A reference to a mutable buffer for use with `Effect` +foreign import data EffectBuffer :: Type + +-- | A reference to a mutable buffer for use with `ST` +-- | +-- | The type parameter represents the memory region which the buffer belongs to. +foreign import data STBuffer :: Region -> Type + +-- | Runs an effect creating an `STBuffer` then freezes the buffer and returns +-- | it, without unneccessary copying. +runST :: forall h. ST h (STBuffer h) -> Buffer +runST st = ST.run (unsafeCoerce st) + +instance mutableBufferEffect :: MutableBuffer EffectBuffer Effect where + create = createImpl + freeze = copyAllImpl + thaw = copyAllImpl + fromArray = fromArrayImpl + fromString = fromStringImpl + fromArrayBuffer = fromArrayBufferImpl + toArrayBuffer = toArrayBufferImpl + read = readImpl + readString = readStringImpl + toString = toStringImpl + write = writeImpl + writeString = writeStringImpl + toArray = toArrayImpl + getAtOffset = getAtOffsetImpl + setAtOffset = setAtOffsetImpl + size = sizeImpl + concat = concatImpl + concat' = concatImpl' + copy = copyImpl + fill = fillImpl + +instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where + create = createImpl + freeze = copyAllImpl + thaw = copyAllImpl + fromArray = fromArrayImpl + fromString = fromStringImpl + fromArrayBuffer = fromArrayBufferImpl + toArrayBuffer = toArrayBufferImpl + read = readImpl + readString = readStringImpl + toString = toStringImpl + write = writeImpl + writeString = writeStringImpl + toArray = toArrayImpl + getAtOffset = getAtOffsetImpl + setAtOffset = setAtOffsetImpl + size = sizeImpl + concat = concatImpl + concat' = concatImpl' + copy = copyImpl + fill = fillImpl + +usingFromFrozen :: forall b e a. (Buffer -> a) -> b -> e a +usingFromFrozen f buf = unsafeCoerce \_ -> f $ unsafeCoerce buf + +usingToFrozen :: forall b e a. (a -> Buffer) -> a -> e b +usingToFrozen f x = unsafeCoerce \_ -> unsafeCoerce $ f x + +createImpl :: forall b e. Int -> e b +createImpl = usingToFrozen Buffer.create + +foreign import copyAllImpl :: forall a b e. a -> e b + +fromArrayImpl :: forall b e. Array Octet -> e b +fromArrayImpl = usingToFrozen Buffer.fromArray + +fromStringImpl :: forall b e. String -> Encoding -> e b +fromStringImpl s = usingToFrozen $ Buffer.fromString s + +fromArrayBufferImpl :: forall b e. ArrayBuffer -> e b +fromArrayBufferImpl = usingToFrozen Buffer.fromArrayBuffer + +toArrayBufferImpl :: forall b e. b -> e ArrayBuffer +toArrayBufferImpl = usingFromFrozen Buffer.toArrayBuffer + +readImpl :: forall b e. BufferValueType -> Offset -> b -> e Int +readImpl t o = usingFromFrozen $ Buffer.read t o + +readStringImpl :: forall b e. Encoding -> Offset -> Offset -> b -> e String +readStringImpl e o o' = usingFromFrozen $ Buffer.readString e o o' + +toStringImpl :: forall b e. Encoding -> b -> e String +toStringImpl e = usingFromFrozen $ Buffer.toString e + +writeImpl :: forall b e. BufferValueType -> Int -> Offset -> b -> e Unit +writeImpl = writeInternal <<< show + +foreign import writeInternal :: forall b e. String -> Int -> Offset -> b -> e Unit + +writeStringImpl :: forall b e. Encoding -> Offset -> Int -> String -> b -> e Int +writeStringImpl = writeStringInternal <<< encodingToNode + +foreign import writeStringInternal :: + forall b e. String -> Offset -> Int -> String -> b -> e Int + +toArrayImpl :: forall b e. b -> e (Array Octet) +toArrayImpl = usingFromFrozen Buffer.toArray + +getAtOffsetImpl :: forall b e. Offset -> b -> e (Maybe Octet) +getAtOffsetImpl o = usingFromFrozen $ Buffer.getAtOffset o + +foreign import setAtOffsetImpl :: forall b e. Octet -> Offset -> b -> e Unit + +sizeImpl :: forall b e. b -> e Int +sizeImpl = usingFromFrozen Buffer.size + +concatImpl :: forall b e. Array b -> e b +concatImpl arrs = unsafeCoerce \_ -> Buffer.concat (unsafeCoerce arrs) + +concatImpl' :: forall b e. Array b -> Int -> e b +concatImpl' arrs n = unsafeCoerce \_ -> Buffer.concat' (unsafeCoerce arrs) n + +foreign import copyImpl :: forall b e. Offset -> Offset -> b -> Offset -> b -> e Int + +foreign import fillImpl :: forall b e. Octet -> Offset -> Offset -> b -> e Unit diff --git a/src/Node/Buffer/Mutable/Unsafe.purs b/src/Node/Buffer/Mutable/Unsafe.purs new file mode 100644 index 0000000..063fd34 --- /dev/null +++ b/src/Node/Buffer/Mutable/Unsafe.purs @@ -0,0 +1,30 @@ +-- | Unsafe operations on mutable buffers. +module Node.Buffer.Mutable.Unsafe + ( class MutableBufferUnsafe + , slice + ) where + +import Effect (Effect) +import Control.Monad.ST (ST, kind Region) +import Node.Buffer as Buffer +import Node.Buffer (Offset) +import Node.Buffer.Mutable (class MutableBuffer, EffectBuffer, STBuffer) +import Unsafe.Coerce (unsafeCoerce) + +class MutableBuffer b e <= MutableBufferUnsafe b e | b -> e, e -> b where + + -- | Creates a new buffer slice that acts like a window on the original buffer. + -- | Writing to the slice buffer updates the original buffer and vice-versa. + -- | + -- | This is considered unsafe as writing to a slice can result in action at a + -- | distance. + slice :: Offset -> Offset -> b -> b + +instance mutableBufferUnsafeEffect :: MutableBufferUnsafe EffectBuffer Effect where + slice = sliceImpl + +instance mutableBufferUnsafeST :: MutableBufferUnsafe (STBuffer h) (ST h) where + slice = sliceImpl + +sliceImpl :: forall b. Offset -> Offset -> b -> b +sliceImpl = unsafeCoerce Buffer.slice diff --git a/src/Node/Buffer/ST.js b/src/Node/Buffer/ST.js deleted file mode 100644 index 845bfe1..0000000 --- a/src/Node/Buffer/ST.js +++ /dev/null @@ -1,74 +0,0 @@ -/* global exports */ -/* global Buffer */ -"use strict"; - -exports.copyImpl = function(a) { - return function() { - return Buffer.from(a); - }; -}; - -exports.writeImpl = function (ty) { - return function (value) { - return function (offset) { - return function (buf) { - return function() { - buf['write' + ty](value, offset); - return {}; - } - }; - }; - }; -}; - -exports.writeStringImpl = function (encoding) { - return function (offset) { - return function (length) { - return function (value) { - return function (buff) { - return function() { - return buff.write(value, offset, length, encoding); - } - }; - }; - }; - }; -}; - -exports.setAtOffset = function (value) { - return function (offset) { - return function (buff) { - return function() { - buff[offset] = value; - return {}; - }; - }; - }; -}; - -exports.copy = function (srcStart) { - return function (srcEnd) { - return function (src) { - return function (targStart) { - return function (targ) { - return function() { - return src.copy(targ, targStart, srcStart, srcEnd); - }; - }; - }; - }; - }; -}; - -exports.fill = function (octet) { - return function (start) { - return function (end) { - return function (buf) { - return function() { - buf.fill(octet, start, end); - return {}; - }; - }; - }; - }; -}; diff --git a/src/Node/Buffer/ST.purs b/src/Node/Buffer/ST.purs deleted file mode 100644 index 1232665..0000000 --- a/src/Node/Buffer/ST.purs +++ /dev/null @@ -1,142 +0,0 @@ --- | Types and functions for working with mutable buffers using the `ST` effect. --- | --- | This module can be used when mutation is a local effect. -module Node.Buffer.ST - ( STBuffer - , run - , create - , freeze - , thaw - , fromArray - , fromString - , fromArrayBuffer - , toArrayBuffer - , read - , readString - , toString - , write - , writeString - , toArray - , getAtOffset - , setAtOffset - , size - , concat - , concat' - , copy - , fill - ) where - -import Prelude - -import Control.Monad.ST (ST, kind Region) -import Control.Monad.ST as ST -import Data.ArrayBuffer.Types (ArrayBuffer) -import Data.Maybe (Maybe) -import Node.Buffer (Buffer, BufferValueType, Octet, Offset) -import Node.Buffer as Buffer -import Node.Encoding (Encoding, encodingToNode) -import Unsafe.Coerce (unsafeCoerce) - --- | A reference to a mutable buffer for use with `ST` --- | --- | The type parameter represents the memory region which the buffer belongs to. -foreign import data STBuffer :: Region -> Type - -usingFromFrozen :: forall a h. (Buffer -> a) -> STBuffer h -> ST h a -usingFromFrozen f buf = unsafeCoerce \_ -> f $ unsafeCoerce buf - -usingToFrozen :: forall a h. (a -> Buffer) -> a -> ST h (STBuffer h) -usingToFrozen f x = unsafeCoerce \_ -> unsafeCoerce $ f x - --- | Runs an effect creating a mutable buffer, then freezes the buffer and returns it, without copying it. -run :: forall h. ST h (STBuffer h) -> Buffer -run st = ST.run (unsafeCoerce st) - --- | Creates a new buffer of the specified size. -create :: forall h. Int -> ST h (STBuffer h) -create = usingToFrozen Buffer.create - --- | Creates an immutable copy of a mutable buffer. -freeze :: forall h. STBuffer h -> ST h Buffer -freeze = copyImpl - --- | Creates a mutable copy of an immutable buffer. -thaw :: forall h. Buffer -> ST h (STBuffer h) -thaw = copyImpl - -foreign import copyImpl :: forall h a b. a -> ST h b - --- | Creates a new buffer from an array of octets, sized to match the array. -fromArray :: forall h. Array Octet -> ST h (STBuffer h) -fromArray = usingToFrozen Buffer.fromArray - --- | Creates a new buffer from a string with the specified encoding, sized to --- | match the string. -fromString :: forall h. String -> Encoding -> ST h (STBuffer h) -fromString s = usingToFrozen $ Buffer.fromString s - --- | Creates a buffer view from a JS ArrayByffer without copying data. -fromArrayBuffer :: forall h. ArrayBuffer -> ST h (STBuffer h) -fromArrayBuffer = usingToFrozen Buffer.fromArrayBuffer - --- | Copies the data in the buffer to a new JS ArrayBuffer -toArrayBuffer :: forall h. STBuffer h -> ST h ArrayBuffer -toArrayBuffer = usingFromFrozen Buffer.toArrayBuffer - --- | Reads a numeric value from a buffer at the specified offset. -read :: forall h. BufferValueType -> Offset -> STBuffer h -> ST h Int -read t o = usingFromFrozen $ Buffer.read t o - --- | Reads a section of a buffer as a string with the specified encoding. -readString :: forall h. Encoding -> Offset -> Offset -> STBuffer h -> ST h String -readString e o o' = usingFromFrozen $ Buffer.readString e o o' - --- | Reads the buffer as a string with the specified encoding. -toString :: forall h. Encoding -> STBuffer h -> ST h String -toString e = usingFromFrozen $ Buffer.toString e - --- | Writes a numeric value to a buffer at the specified offset. -write :: forall h. BufferValueType -> Int -> Offset -> STBuffer h -> ST h Unit -write = writeImpl <<< show - -foreign import writeImpl :: forall h. String -> Int -> Offset -> STBuffer h -> ST h Unit - --- | Writes octets from a string to a buffer at the specified offset. Multi-byte --- | characters will not be written to the buffer if there is not enough capacity --- | to write them fully. The number of bytes written is returned. -writeString :: forall h. Encoding -> Offset -> Int -> String -> STBuffer h -> ST h Int -writeString = writeStringImpl <<< encodingToNode - -foreign import writeStringImpl :: - forall h. String -> Offset -> Int -> String -> STBuffer h -> ST h Int - --- | Creates an array of octets from a buffer's contents. -toArray :: forall h. STBuffer h -> ST h (Array Octet) -toArray = usingFromFrozen Buffer.toArray - --- | Reads an octet from a buffer at the specified offset. -getAtOffset :: forall h. Offset -> STBuffer h -> ST h (Maybe Octet) -getAtOffset o = usingFromFrozen $ Buffer.getAtOffset o - --- | Writes an octet in the buffer at the specified offset. -foreign import setAtOffset :: forall h. Offset -> Offset -> STBuffer h -> ST h Unit - --- | Returns the size of a buffer. -size :: forall h. STBuffer h -> ST h Int -size = usingFromFrozen Buffer.size - --- | Concatenates a list of buffers. -concat :: forall h. Array (STBuffer h) -> ST h (STBuffer h) -concat arrs = unsafeCoerce \_ -> Buffer.concat (unsafeCoerce arrs) - --- | Concatenates a list of buffers, combining them into a new buffer of the --- | specified length. -concat' :: forall h. Array (STBuffer h) -> Int -> ST h (STBuffer h) -concat' arrs n = unsafeCoerce \_ -> Buffer.concat' (unsafeCoerce arrs) n - --- | Copies a section of a source buffer into a target buffer at the specified --- | offset, and returns the number of octets copied. -foreign import copy :: forall h. Offset -> Offset -> STBuffer h -> Offset -> STBuffer h -> ST h Int - --- | Fills a range in a buffer with the specified octet. -foreign import fill :: forall h. Octet -> Offset -> Offset -> STBuffer h -> ST h Unit diff --git a/src/Node/Buffer/ST/Unsafe.purs b/src/Node/Buffer/ST/Unsafe.purs deleted file mode 100644 index c11cf7f..0000000 --- a/src/Node/Buffer/ST/Unsafe.purs +++ /dev/null @@ -1,14 +0,0 @@ -module Node.Buffer.ST.Unsafe where - -import Node.Buffer as Buffer -import Node.Buffer (Offset) -import Node.Buffer.ST (STBuffer) -import Unsafe.Coerce (unsafeCoerce) - --- | Creates a new buffer slice that acts like a window on the original buffer. --- | Writing to the slice buffer updates the original buffer and vice-versa. --- --- | This is considered unsafe as writing to a slice can result in action at a --- | distance. -slice :: forall h. Offset -> Offset -> STBuffer h -> STBuffer h -slice = unsafeCoerce Buffer.slice diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 9098e88..d8d68aa 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -3,11 +3,9 @@ module Test.Main where import Prelude import Effect (Effect) import Test.Node.Buffer as Buffer -import Test.Node.Buffer.Effect as Effect -import Test.Node.Buffer.ST as ST +import Test.Node.Buffer.Mutable as Mutable main :: Effect Unit main = do Buffer.test - Effect.test - ST.test + Mutable.test diff --git a/test/Test/Node/Buffer/Effect.purs b/test/Test/Node/Buffer/Effect.purs deleted file mode 100644 index a489e06..0000000 --- a/test/Test/Node/Buffer/Effect.purs +++ /dev/null @@ -1,161 +0,0 @@ -module Test.Node.Buffer.Effect (test) where - -import Prelude - -import Data.Maybe (Maybe(..)) -import Data.Traversable (traverse) -import Effect (Effect) -import Effect.Console (log) -import Node.Buffer as Buffer -import Node.Buffer (BufferValueType(..)) -import Node.Buffer.Effect (toArray, concat', fromArray, fill, copy, readString, fromString, toString, freeze, thaw, read, write, create, getAtOffset) -import Node.Encoding (Encoding(..)) -import Test.Assert (assertEqual) - -test :: Effect Unit -test = do - log "Testing Node.Buffer.Effect ..." - - log " - create" - testCreate - - log " - freeze" - testFreeze - - log " - thaw" - testThaw - - log " - Reading and writing" - testReadWrite - - log " - fromArray" - testFromArray - - log " - toArray" - testToArray - - log " - fromString" - testFromString - - log " - toString" - testToString - - log " - readString" - testReadString - - log " - copy" - testCopy - - log " - fill" - testFill - - log " - concat'" - testConcat' - - log " - getAtOffset" - testGetAtOffset - -testCreate :: Effect Unit -testCreate = do - buf <- create 3 >>= toArray - assertEqual {expected: [0, 0, 0], actual: buf} - -testFreeze :: Effect Unit -testFreeze = do - buf <- fromArray [1, 2, 3] >>= freeze - assertEqual {expected: [1, 2, 3], actual: Buffer.toArray buf} - -testThaw :: Effect Unit -testThaw = do - buf <- (thaw $ Buffer.fromArray [1, 2, 3]) >>= toArray - assertEqual {expected: [1, 2, 3], actual: buf} - -testReadWrite :: Effect Unit -testReadWrite = do - buf <- create 1 - let val = 42 - write UInt8 val 0 buf - readVal <- read UInt8 0 buf - - assertEqual {expected: val, actual: readVal} - -testFromArray :: Effect Unit -testFromArray = do - buf <- fromArray [1,2,3,4,5] - readVal <- read UInt8 2 buf - - assertEqual {expected: 3, actual: readVal} - -testToArray :: Effect Unit -testToArray = do - let val = [1,2,67,3,3,7,8,3,4,237] - - buf <- fromArray val - valOut <- toArray buf - - assertEqual {expected: val, actual: valOut} - -testFromString :: Effect Unit -testFromString = do - let str = "hello, world" - - buf <- fromString str ASCII - val <- read UInt8 6 buf - - assertEqual {expected: 32, actual: val} -- ASCII space - -testToString :: Effect Unit -testToString = do - let str = "hello, world" - - buf <- fromString str ASCII - strOut <- toString ASCII buf - - assertEqual {expected: str, actual: strOut} - -testReadString :: Effect Unit -testReadString = do - let str = "hello, world" - - buf <- fromString str ASCII - strOut <- readString ASCII 7 12 buf - - assertEqual {expected: "world", actual: strOut} - -testCopy :: Effect Unit -testCopy = do - buf1 <- fromArray [1,2,3,4,5] - buf2 <- fromArray [10,9,8,7,6] - - copied <- copy 0 3 buf1 2 buf2 - out <- toArray buf2 - - assertEqual {expected: 3, actual: copied} - assertEqual {expected: [10,9,1,2,3], actual: out} - -testFill :: Effect Unit -testFill = do - buf <- fromArray [1,1,1,1,1] - fill 42 2 4 buf - out <- toArray buf - - assertEqual {expected: [1,1,42,42,1], actual: out} - -testConcat' :: Effect Unit -testConcat' = do - bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] - buf <- concat' bufs 15 - out <- toArray buf - - assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} - -testGetAtOffset :: Effect Unit -testGetAtOffset = do - buf <- fromArray [1, 2, 3, 4] - o1 <- getAtOffset 1 buf - o4 <- getAtOffset 4 buf - om1 <- getAtOffset (-1) buf - - assertEqual {expected: Just 2, actual: o1} - assertEqual {expected: Nothing, actual: o4} - assertEqual {expected: Nothing, actual: om1} diff --git a/test/Test/Node/Buffer/Mutable.purs b/test/Test/Node/Buffer/Mutable.purs new file mode 100644 index 0000000..78f1f5c --- /dev/null +++ b/test/Test/Node/Buffer/Mutable.purs @@ -0,0 +1,203 @@ +module Test.Node.Buffer.Mutable (test) where + +import Prelude + +import Control.Monad.ST as ST +import Data.Maybe (Maybe(..)) +import Data.Traversable (traverse) +import Effect (Effect) +import Effect.Console (log) +import Node.Buffer (BufferValueType(..)) +import Node.Buffer as Buffer +import Node.Buffer.Mutable (class MutableBuffer, EffectBuffer, STBuffer, concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, toArrayBuffer, fromString, getAtOffset, read, readString, runST, thaw, toArray, toString, write) +import Node.Encoding (Encoding(..)) +import Test.Assert (assertEqual) +import Test.Node.Buffer.Mutable.Unsafe as Unsafe +import Type.Proxy (Proxy(..)) +import Unsafe.Coerce (unsafeCoerce) + +test :: Effect Unit +test = do + + log "Testing Node.Buffer.Mutable [EffectBuffer] ..." + testMutableBuffer (Proxy :: Proxy EffectBuffer) identity + + log "Testing Node.Buffer.Mutable [STBuffer] ..." + testMutableBuffer (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) + log " - runST" + testRunSt + + Unsafe.test + +testMutableBuffer :: forall b e. Monad e => MutableBuffer b e => + Proxy b -> (forall a. e a -> Effect a) -> Effect Unit +testMutableBuffer _ run = do + + log " - create" + testCreate + + log " - freeze" + testFreeze + + log " - thaw" + testThaw + + log " - Reading and writing" + testReadWrite + + log " - fromArray" + testFromArray + + log " - toArray" + testToArray + + log " - fromString" + testFromString + + log " - (to/from)ArrayBuffer" + testToFromArrayBuffer + + log " - toString" + testToString + + log " - readString" + testReadString + + log " - copy" + testCopy + + log " - fill" + testFill + + log " - concat'" + testConcat' + + log " - getAtOffset" + testGetAtOffset + + where + testCreate :: Effect Unit + testCreate = do + buf <- run ((create 3 :: e b) >>= toArray) + assertEqual {expected: [0, 0, 0], actual: buf} + + testFreeze :: Effect Unit + testFreeze = do + buf <- Buffer.toArray <$> run ((fromArray [1, 2, 3] :: e b) >>= freeze) + assertEqual {expected: [1, 2, 3], actual: buf} + + testThaw :: Effect Unit + testThaw = do + buf <- run ((thaw (Buffer.fromArray [1, 2, 3]) :: e b) >>= toArray) + assertEqual {expected: [1, 2, 3], actual: buf} + + testReadWrite :: Effect Unit + testReadWrite = do + let val = 42 + readVal <- run do + buf <- create 1 :: e b + write UInt8 val 0 buf + read UInt8 0 buf + + assertEqual {expected: val, actual: readVal} + + testFromArray :: Effect Unit + testFromArray = do + readVal <- run do + buf <- fromArray [1,2,3,4,5] :: e b + read UInt8 2 buf + + assertEqual {expected: 3, actual: readVal} + + testToArray :: Effect Unit + testToArray = do + let val = [1,2,67,3,3,7,8,3,4,237] + valOut <- run do + buf <- fromArray val :: e b + toArray buf + + assertEqual {expected: val, actual: valOut} + + testFromString :: Effect Unit + testFromString = do + let str = "hello, world" + val <- run do + buf <- fromString str ASCII :: e b + read UInt8 6 buf + + assertEqual {expected: 32, actual: val} -- ASCII space + + testToFromArrayBuffer :: Effect Unit + testToFromArrayBuffer = do + buf <- run $ + fromArray [1, 2, 3] + >>= toArrayBuffer + >>= fromArrayBuffer + >>= toArray + assertEqual {expected: [1, 2, 3], actual: buf} + + testToString :: Effect Unit + testToString = do + let str = "hello, world" + strOut <-run do + buf <- fromString str ASCII :: e b + toString ASCII buf + + assertEqual {expected: str, actual: strOut} + + testReadString :: Effect Unit + testReadString = do + let str = "hello, world" + strOut <- run do + buf <- fromString str ASCII :: e b + readString ASCII 7 12 buf + + assertEqual {expected: "world", actual: strOut} + + testCopy :: Effect Unit + testCopy = do + {copied, out} <- run do + buf1 <- fromArray [1,2,3,4,5] :: e b + buf2 <- fromArray [10,9,8,7,6] + copied <- copy 0 3 buf1 2 buf2 + out <- toArray buf2 + pure {copied, out} + + assertEqual {expected: 3, actual: copied} + assertEqual {expected: [10,9,1,2,3], actual: out} + + testFill :: Effect Unit + testFill = do + let out = ST.run do + buf <- fromArray [1,1,1,1,1] + fill 42 2 4 buf + toArray buf + + assertEqual {expected: [1,1,42,42,1], actual: out} + + testConcat' :: Effect Unit + testConcat' = do + out <- run do + bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] + buf <- concat' bufs 15 :: e b + toArray buf + + assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} + + testGetAtOffset :: Effect Unit + testGetAtOffset = do + {o1, o4, om1} <- run do + buf <- fromArray [1, 2, 3, 4] :: e b + o1 <- getAtOffset 1 buf + o4 <- getAtOffset 4 buf + om1 <- getAtOffset (-1) buf + pure {o1, o4, om1} + + assertEqual {expected: Just 2, actual: o1} + assertEqual {expected: Nothing, actual: o4} + assertEqual {expected: Nothing, actual: om1} + +testRunSt :: Effect Unit +testRunSt = do + let buf = Buffer.toArray $ runST (create 3) + assertEqual {expected: [0, 0, 0], actual: buf} diff --git a/test/Test/Node/Buffer/Mutable/Unsafe.purs b/test/Test/Node/Buffer/Mutable/Unsafe.purs new file mode 100644 index 0000000..8addb33 --- /dev/null +++ b/test/Test/Node/Buffer/Mutable/Unsafe.purs @@ -0,0 +1,41 @@ +module Test.Node.Buffer.Mutable.Unsafe (test) where + +import Prelude + +import Effect (Effect) +import Effect.Console (log) +import Control.Monad.ST as ST +import Node.Buffer.Mutable (EffectBuffer, STBuffer, fromArray, setAtOffset, toArray) +import Node.Buffer.Mutable.Unsafe (class MutableBufferUnsafe, slice) +import Test.Assert (assertEqual) +import Type.Proxy (Proxy(..)) +import Unsafe.Coerce (unsafeCoerce) + +test :: Effect Unit +test = do + log "Testing Node.Buffer.Mutable.Unsafe [EffectBuffer] ..." + testMutableBufferUnsafe (Proxy :: Proxy EffectBuffer) identity + + log "Testing Node.Buffer.Mutable.Unsafe [STBuffer] ..." + testMutableBufferUnsafe (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) + +testMutableBufferUnsafe :: forall b e. Monad e => MutableBufferUnsafe b e => + Proxy b -> (forall a. e a -> Effect a) -> Effect Unit +testMutableBufferUnsafe _ run = do + + log " - slice" + testSlice + + where + testSlice :: Effect Unit + testSlice = do + {bufArr, bufSliceArr} <- run do + buf <- fromArray [1, 2, 3, 4] :: e b + let bufSlice = slice 1 3 buf :: b + setAtOffset 42 1 bufSlice + bufArr <- toArray buf + bufSliceArr <- toArray bufSlice + pure {bufArr, bufSliceArr} + + assertEqual {expected: [1, 2, 42, 4], actual: bufArr} + assertEqual {expected: [2, 42], actual: bufSliceArr} diff --git a/test/Test/Node/Buffer/ST.purs b/test/Test/Node/Buffer/ST.purs deleted file mode 100644 index 44edd06..0000000 --- a/test/Test/Node/Buffer/ST.purs +++ /dev/null @@ -1,169 +0,0 @@ -module Test.Node.Buffer.ST (test) where - -import Prelude - -import Control.Monad.ST as ST -import Data.Maybe (Maybe(..)) -import Data.Traversable (traverse) -import Effect (Effect) -import Effect.Console (log) -import Node.Buffer as Buffer -import Node.Buffer (BufferValueType(..)) -import Node.Buffer.ST (freeze, thaw, concat', copy, create, fill, fromArray, fromString, getAtOffset, read, readString, run, toArray, toString, write) -import Node.Encoding (Encoding(..)) -import Test.Assert (assertEqual) - -test :: Effect Unit -test = do - log "Testing Node.Buffer.ST ..." - - log " - create" - testCreate - - log " - freeze" - testFreeze - - log " - thaw" - testThaw - - log " - Reading and writing" - testReadWrite - - log " - fromArray" - testFromArray - - log " - toArray" - testToArray - - log " - fromString" - testFromString - - log " - toString" - testToString - - log " - readString" - testReadString - - log " - copy" - testCopy - - log " - fill" - testFill - - log " - concat'" - testConcat' - - log " - getAtOffset" - testGetAtOffset - -testCreate :: Effect Unit -testCreate = do - let buf = run (create 3) - assertEqual {expected: [0, 0, 0], actual: Buffer.toArray buf} - -testFreeze :: Effect Unit -testFreeze = do - let buf = ST.run (fromArray [1, 2, 3] >>= freeze) - assertEqual {expected: [1, 2, 3], actual: Buffer.toArray buf} - -testThaw :: Effect Unit -testThaw = do - let buf = run (thaw $ Buffer.fromArray [1, 2, 3]) - assertEqual {expected: [1, 2, 3], actual: Buffer.toArray buf} - -testReadWrite :: Effect Unit -testReadWrite = do - let val = 42 - readVal = ST.run do - buf <- create 1 - write UInt8 val 0 buf - read UInt8 0 buf - - assertEqual {expected: val, actual: readVal} - -testFromArray :: Effect Unit -testFromArray = do - let readVal = ST.run do - buf <- fromArray [1,2,3,4,5] - read UInt8 2 buf - - assertEqual {expected: 3, actual: readVal} - -testToArray :: Effect Unit -testToArray = do - let val = [1,2,67,3,3,7,8,3,4,237] - valOut = ST.run do - buf <- fromArray val - toArray buf - - assertEqual {expected: val, actual: valOut} - -testFromString :: Effect Unit -testFromString = do - let str = "hello, world" - val = ST.run do - buf <- fromString str ASCII - read UInt8 6 buf - - assertEqual {expected: 32, actual: val} -- ASCII space - -testToString :: Effect Unit -testToString = do - let str = "hello, world" - strOut = ST.run do - buf <- fromString str ASCII - toString ASCII buf - - assertEqual {expected: str, actual: strOut} - -testReadString :: Effect Unit -testReadString = do - let str = "hello, world" - strOut = ST.run do - buf <- fromString str ASCII - readString ASCII 7 12 buf - - assertEqual {expected: "world", actual: strOut} - -testCopy :: Effect Unit -testCopy = do - let {copied, out} = ST.run do - buf1 <- fromArray [1,2,3,4,5] - buf2 <- fromArray [10,9,8,7,6] - copied <- copy 0 3 buf1 2 buf2 - out <- toArray buf2 - pure {copied, out} - - assertEqual {expected: 3, actual: copied} - assertEqual {expected: [10,9,1,2,3], actual: out} - -testFill :: Effect Unit -testFill = do - let out = ST.run do - buf <- fromArray [1,1,1,1,1] - fill 42 2 4 buf - toArray buf - - assertEqual {expected: [1,1,42,42,1], actual: out} - -testConcat' :: Effect Unit -testConcat' = do - let out = ST.run do - bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] - buf <- concat' bufs 15 - toArray buf - - assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} - -testGetAtOffset :: Effect Unit -testGetAtOffset = do - let {o1, o4, om1} = ST.run do - buf <- fromArray [1, 2, 3, 4] - o1 <- getAtOffset 1 buf - o4 <- getAtOffset 4 buf - om1 <- getAtOffset (-1) buf - pure {o1, o4, om1} - - assertEqual {expected: Just 2, actual: o1} - assertEqual {expected: Nothing, actual: o4} - assertEqual {expected: Nothing, actual: om1} From 9b48b294faee45bfee4e1372ecb14e1d22527dcd Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sat, 5 Jan 2019 13:17:35 +0000 Subject: [PATCH 03/11] Add Monad constraint and rename type variables on MutableBuffer class. As requested in #discussion_r244534678 --- src/Node/Buffer/Mutable.purs | 96 +++++++++++------------ src/Node/Buffer/Mutable/Unsafe.purs | 6 +- test/Test/Node/Buffer/Mutable.purs | 28 +++---- test/Test/Node/Buffer/Mutable/Unsafe.purs | 8 +- 4 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/Node/Buffer/Mutable.purs b/src/Node/Buffer/Mutable.purs index db4a487..31c54b2 100644 --- a/src/Node/Buffer/Mutable.purs +++ b/src/Node/Buffer/Mutable.purs @@ -37,74 +37,74 @@ import Node.Buffer as Buffer import Node.Encoding (Encoding, encodingToNode) import Unsafe.Coerce (unsafeCoerce) --- | A type class for mutable buffers `b` where operations on those buffers are --- | represented by a particular effect type `e`. -class MutableBuffer b e | e -> b, b -> e where +-- | A type class for mutable buffers `buf` where operations on those buffers are +-- | represented by a particular monadic effect type `m`. +class Monad m <= MutableBuffer buf m | m -> buf, buf -> m where -- | Creates a new buffer of the specified size. - create :: Int -> e b + create :: Int -> m buf -- | Creates an immutable copy of a mutable buffer. - freeze :: b -> e Buffer + freeze :: buf -> m Buffer -- | Creates a mutable copy of an immutable buffer. - thaw :: Buffer -> e b + thaw :: Buffer -> m buf -- | Creates a new buffer from an array of octets, sized to match the array. - fromArray :: Array Octet -> e b + fromArray :: Array Octet -> m buf -- | Creates a new buffer from a string with the specified encoding, sized to -- | match the string. - fromString :: String -> Encoding -> e b + fromString :: String -> Encoding -> m buf -- | Creates a buffer view from a JS ArrayByffer without copying data. - fromArrayBuffer :: ArrayBuffer -> e b + fromArrayBuffer :: ArrayBuffer -> m buf -- | Copies the data in the buffer to a new JS ArrayBuffer - toArrayBuffer :: b -> e ArrayBuffer + toArrayBuffer :: buf -> m ArrayBuffer -- | Reads a numeric value from a buffer at the specified offset. - read :: BufferValueType -> Offset -> b -> e Int + read :: BufferValueType -> Offset -> buf -> m Int -- | Reads a section of a buffer as a string with the specified encoding. - readString :: Encoding -> Offset -> Offset -> b -> e String + readString :: Encoding -> Offset -> Offset -> buf -> m String -- | Reads the buffer as a string with the specified encoding. - toString :: Encoding -> b -> e String + toString :: Encoding -> buf -> m String -- | Writes a numeric value to a buffer at the specified offset. - write :: BufferValueType -> Int -> Offset -> b -> e Unit + write :: BufferValueType -> Int -> Offset -> buf -> m Unit -- | Writes octets from a string to a buffer at the specified offset. Multi-byte -- | characters will not be written to the buffer if there is not enough capacity -- | to write them fully. The number of bytes written is returned. - writeString :: Encoding -> Offset -> Int -> String -> b -> e Int + writeString :: Encoding -> Offset -> Int -> String -> buf -> m Int -- | Creates an array of octets from a buffer's contents. - toArray :: b -> e (Array Octet) + toArray :: buf -> m (Array Octet) -- | Reads an octet from a buffer at the specified offset. - getAtOffset :: Offset -> b -> e (Maybe Octet) + getAtOffset :: Offset -> buf -> m (Maybe Octet) -- | Writes an octet in the buffer at the specified offset. - setAtOffset :: Octet -> Offset -> b -> e Unit + setAtOffset :: Octet -> Offset -> buf -> m Unit -- | Returns the size of a buffer. - size :: b -> e Int + size :: buf -> m Int -- | Concatenates a list of buffers. - concat :: Array b -> e b + concat :: Array buf -> m buf -- | Concatenates a list of buffers, combining them into a new buffer of the -- | specified length. - concat' :: Array b -> Int -> e b + concat' :: Array buf -> Int -> m buf -- | Copies a section of a source buffer into a target buffer at the specified -- | offset, and returns the number of octets copied. - copy :: Offset -> Offset -> b -> Offset -> b -> e Int + copy :: Offset -> Offset -> buf -> Offset -> buf -> m Int -- | Fills a range in a buffer with the specified octet. - fill :: Octet -> Offset -> Offset -> b -> e Unit + fill :: Octet -> Offset -> Offset -> buf -> m Unit -- | A reference to a mutable buffer for use with `Effect` foreign import data EffectBuffer :: Type @@ -163,66 +163,66 @@ instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where copy = copyImpl fill = fillImpl -usingFromFrozen :: forall b e a. (Buffer -> a) -> b -> e a +usingFromFrozen :: forall buf m a. (Buffer -> a) -> buf -> m a usingFromFrozen f buf = unsafeCoerce \_ -> f $ unsafeCoerce buf -usingToFrozen :: forall b e a. (a -> Buffer) -> a -> e b +usingToFrozen :: forall buf m a. (a -> Buffer) -> a -> m buf usingToFrozen f x = unsafeCoerce \_ -> unsafeCoerce $ f x -createImpl :: forall b e. Int -> e b +createImpl :: forall buf m. Int -> m buf createImpl = usingToFrozen Buffer.create -foreign import copyAllImpl :: forall a b e. a -> e b +foreign import copyAllImpl :: forall a buf m. a -> m buf -fromArrayImpl :: forall b e. Array Octet -> e b +fromArrayImpl :: forall buf m. Array Octet -> m buf fromArrayImpl = usingToFrozen Buffer.fromArray -fromStringImpl :: forall b e. String -> Encoding -> e b +fromStringImpl :: forall buf m. String -> Encoding -> m buf fromStringImpl s = usingToFrozen $ Buffer.fromString s -fromArrayBufferImpl :: forall b e. ArrayBuffer -> e b +fromArrayBufferImpl :: forall buf m. ArrayBuffer -> m buf fromArrayBufferImpl = usingToFrozen Buffer.fromArrayBuffer -toArrayBufferImpl :: forall b e. b -> e ArrayBuffer +toArrayBufferImpl :: forall buf m. buf -> m ArrayBuffer toArrayBufferImpl = usingFromFrozen Buffer.toArrayBuffer -readImpl :: forall b e. BufferValueType -> Offset -> b -> e Int +readImpl :: forall buf m. BufferValueType -> Offset -> buf -> m Int readImpl t o = usingFromFrozen $ Buffer.read t o -readStringImpl :: forall b e. Encoding -> Offset -> Offset -> b -> e String -readStringImpl e o o' = usingFromFrozen $ Buffer.readString e o o' +readStringImpl :: forall buf m. Encoding -> Offset -> Offset -> buf -> m String +readStringImpl m o o' = usingFromFrozen $ Buffer.readString m o o' -toStringImpl :: forall b e. Encoding -> b -> e String -toStringImpl e = usingFromFrozen $ Buffer.toString e +toStringImpl :: forall buf m. Encoding -> buf -> m String +toStringImpl m = usingFromFrozen $ Buffer.toString m -writeImpl :: forall b e. BufferValueType -> Int -> Offset -> b -> e Unit +writeImpl :: forall buf m. BufferValueType -> Int -> Offset -> buf -> m Unit writeImpl = writeInternal <<< show -foreign import writeInternal :: forall b e. String -> Int -> Offset -> b -> e Unit +foreign import writeInternal :: forall buf m. String -> Int -> Offset -> buf -> m Unit -writeStringImpl :: forall b e. Encoding -> Offset -> Int -> String -> b -> e Int +writeStringImpl :: forall buf m. Encoding -> Offset -> Int -> String -> buf -> m Int writeStringImpl = writeStringInternal <<< encodingToNode foreign import writeStringInternal :: - forall b e. String -> Offset -> Int -> String -> b -> e Int + forall buf m. String -> Offset -> Int -> String -> buf -> m Int -toArrayImpl :: forall b e. b -> e (Array Octet) +toArrayImpl :: forall buf m. buf -> m (Array Octet) toArrayImpl = usingFromFrozen Buffer.toArray -getAtOffsetImpl :: forall b e. Offset -> b -> e (Maybe Octet) +getAtOffsetImpl :: forall buf m. Offset -> buf -> m (Maybe Octet) getAtOffsetImpl o = usingFromFrozen $ Buffer.getAtOffset o -foreign import setAtOffsetImpl :: forall b e. Octet -> Offset -> b -> e Unit +foreign import setAtOffsetImpl :: forall buf m. Octet -> Offset -> buf -> m Unit -sizeImpl :: forall b e. b -> e Int +sizeImpl :: forall buf m. buf -> m Int sizeImpl = usingFromFrozen Buffer.size -concatImpl :: forall b e. Array b -> e b +concatImpl :: forall buf m. Array buf -> m buf concatImpl arrs = unsafeCoerce \_ -> Buffer.concat (unsafeCoerce arrs) -concatImpl' :: forall b e. Array b -> Int -> e b +concatImpl' :: forall buf m. Array buf -> Int -> m buf concatImpl' arrs n = unsafeCoerce \_ -> Buffer.concat' (unsafeCoerce arrs) n -foreign import copyImpl :: forall b e. Offset -> Offset -> b -> Offset -> b -> e Int +foreign import copyImpl :: forall buf m. Offset -> Offset -> buf -> Offset -> buf -> m Int -foreign import fillImpl :: forall b e. Octet -> Offset -> Offset -> b -> e Unit +foreign import fillImpl :: forall buf m. Octet -> Offset -> Offset -> buf -> m Unit diff --git a/src/Node/Buffer/Mutable/Unsafe.purs b/src/Node/Buffer/Mutable/Unsafe.purs index 063fd34..552d4b9 100644 --- a/src/Node/Buffer/Mutable/Unsafe.purs +++ b/src/Node/Buffer/Mutable/Unsafe.purs @@ -11,14 +11,14 @@ import Node.Buffer (Offset) import Node.Buffer.Mutable (class MutableBuffer, EffectBuffer, STBuffer) import Unsafe.Coerce (unsafeCoerce) -class MutableBuffer b e <= MutableBufferUnsafe b e | b -> e, e -> b where +class MutableBuffer buf m <= MutableBufferUnsafe buf m | buf -> m, m -> buf where -- | Creates a new buffer slice that acts like a window on the original buffer. -- | Writing to the slice buffer updates the original buffer and vice-versa. -- | -- | This is considered unsafe as writing to a slice can result in action at a -- | distance. - slice :: Offset -> Offset -> b -> b + slice :: Offset -> Offset -> buf -> buf instance mutableBufferUnsafeEffect :: MutableBufferUnsafe EffectBuffer Effect where slice = sliceImpl @@ -26,5 +26,5 @@ instance mutableBufferUnsafeEffect :: MutableBufferUnsafe EffectBuffer Effect wh instance mutableBufferUnsafeST :: MutableBufferUnsafe (STBuffer h) (ST h) where slice = sliceImpl -sliceImpl :: forall b. Offset -> Offset -> b -> b +sliceImpl :: forall buf. Offset -> Offset -> buf -> buf sliceImpl = unsafeCoerce Buffer.slice diff --git a/test/Test/Node/Buffer/Mutable.purs b/test/Test/Node/Buffer/Mutable.purs index 78f1f5c..f8d5ae2 100644 --- a/test/Test/Node/Buffer/Mutable.purs +++ b/test/Test/Node/Buffer/Mutable.purs @@ -29,8 +29,8 @@ test = do Unsafe.test -testMutableBuffer :: forall b e. Monad e => MutableBuffer b e => - Proxy b -> (forall a. e a -> Effect a) -> Effect Unit +testMutableBuffer :: forall buf m. MutableBuffer buf m => + Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit testMutableBuffer _ run = do log " - create" @@ -78,24 +78,24 @@ testMutableBuffer _ run = do where testCreate :: Effect Unit testCreate = do - buf <- run ((create 3 :: e b) >>= toArray) + buf <- run ((create 3 :: m buf) >>= toArray) assertEqual {expected: [0, 0, 0], actual: buf} testFreeze :: Effect Unit testFreeze = do - buf <- Buffer.toArray <$> run ((fromArray [1, 2, 3] :: e b) >>= freeze) + buf <- Buffer.toArray <$> run ((fromArray [1, 2, 3] :: m buf) >>= freeze) assertEqual {expected: [1, 2, 3], actual: buf} testThaw :: Effect Unit testThaw = do - buf <- run ((thaw (Buffer.fromArray [1, 2, 3]) :: e b) >>= toArray) + buf <- run ((thaw (Buffer.fromArray [1, 2, 3]) :: m buf) >>= toArray) assertEqual {expected: [1, 2, 3], actual: buf} testReadWrite :: Effect Unit testReadWrite = do let val = 42 readVal <- run do - buf <- create 1 :: e b + buf <- create 1 :: m buf write UInt8 val 0 buf read UInt8 0 buf @@ -104,7 +104,7 @@ testMutableBuffer _ run = do testFromArray :: Effect Unit testFromArray = do readVal <- run do - buf <- fromArray [1,2,3,4,5] :: e b + buf <- fromArray [1,2,3,4,5] :: m buf read UInt8 2 buf assertEqual {expected: 3, actual: readVal} @@ -113,7 +113,7 @@ testMutableBuffer _ run = do testToArray = do let val = [1,2,67,3,3,7,8,3,4,237] valOut <- run do - buf <- fromArray val :: e b + buf <- fromArray val :: m buf toArray buf assertEqual {expected: val, actual: valOut} @@ -122,7 +122,7 @@ testMutableBuffer _ run = do testFromString = do let str = "hello, world" val <- run do - buf <- fromString str ASCII :: e b + buf <- fromString str ASCII :: m buf read UInt8 6 buf assertEqual {expected: 32, actual: val} -- ASCII space @@ -140,7 +140,7 @@ testMutableBuffer _ run = do testToString = do let str = "hello, world" strOut <-run do - buf <- fromString str ASCII :: e b + buf <- fromString str ASCII :: m buf toString ASCII buf assertEqual {expected: str, actual: strOut} @@ -149,7 +149,7 @@ testMutableBuffer _ run = do testReadString = do let str = "hello, world" strOut <- run do - buf <- fromString str ASCII :: e b + buf <- fromString str ASCII :: m buf readString ASCII 7 12 buf assertEqual {expected: "world", actual: strOut} @@ -157,7 +157,7 @@ testMutableBuffer _ run = do testCopy :: Effect Unit testCopy = do {copied, out} <- run do - buf1 <- fromArray [1,2,3,4,5] :: e b + buf1 <- fromArray [1,2,3,4,5] :: m buf buf2 <- fromArray [10,9,8,7,6] copied <- copy 0 3 buf1 2 buf2 out <- toArray buf2 @@ -179,7 +179,7 @@ testMutableBuffer _ run = do testConcat' = do out <- run do bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] - buf <- concat' bufs 15 :: e b + buf <- concat' bufs 15 :: m buf toArray buf assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} @@ -187,7 +187,7 @@ testMutableBuffer _ run = do testGetAtOffset :: Effect Unit testGetAtOffset = do {o1, o4, om1} <- run do - buf <- fromArray [1, 2, 3, 4] :: e b + buf <- fromArray [1, 2, 3, 4] :: m buf o1 <- getAtOffset 1 buf o4 <- getAtOffset 4 buf om1 <- getAtOffset (-1) buf diff --git a/test/Test/Node/Buffer/Mutable/Unsafe.purs b/test/Test/Node/Buffer/Mutable/Unsafe.purs index 8addb33..32ccdd2 100644 --- a/test/Test/Node/Buffer/Mutable/Unsafe.purs +++ b/test/Test/Node/Buffer/Mutable/Unsafe.purs @@ -19,8 +19,8 @@ test = do log "Testing Node.Buffer.Mutable.Unsafe [STBuffer] ..." testMutableBufferUnsafe (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) -testMutableBufferUnsafe :: forall b e. Monad e => MutableBufferUnsafe b e => - Proxy b -> (forall a. e a -> Effect a) -> Effect Unit +testMutableBufferUnsafe :: forall buf m. MutableBufferUnsafe buf m => + Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit testMutableBufferUnsafe _ run = do log " - slice" @@ -30,8 +30,8 @@ testMutableBufferUnsafe _ run = do testSlice :: Effect Unit testSlice = do {bufArr, bufSliceArr} <- run do - buf <- fromArray [1, 2, 3, 4] :: e b - let bufSlice = slice 1 3 buf :: b + buf <- fromArray [1, 2, 3, 4] :: m buf + let bufSlice = slice 1 3 buf :: buf setAtOffset 42 1 bufSlice bufArr <- toArray buf bufSliceArr <- toArray bufSlice From 398aad5dffff8e71d388eba9379cc3d30ad0b5e3 Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sat, 5 Jan 2019 13:33:46 +0000 Subject: [PATCH 04/11] Make slice a safe method, and delete the Unsafe module. As requested in #discussion_r244536851 --- src/Node/Buffer/Mutable.purs | 10 ++++++ src/Node/Buffer/Mutable/Unsafe.purs | 30 ----------------- test/Test/Node/Buffer/Mutable.purs | 21 +++++++++--- test/Test/Node/Buffer/Mutable/Unsafe.purs | 41 ----------------------- 4 files changed, 27 insertions(+), 75 deletions(-) delete mode 100644 src/Node/Buffer/Mutable/Unsafe.purs delete mode 100644 test/Test/Node/Buffer/Mutable/Unsafe.purs diff --git a/src/Node/Buffer/Mutable.purs b/src/Node/Buffer/Mutable.purs index 31c54b2..d5717b6 100644 --- a/src/Node/Buffer/Mutable.purs +++ b/src/Node/Buffer/Mutable.purs @@ -15,6 +15,7 @@ module Node.Buffer.Mutable , toArray , getAtOffset , setAtOffset + , slice , size , concat , concat' @@ -89,6 +90,10 @@ class Monad m <= MutableBuffer buf m | m -> buf, buf -> m where -- | Writes an octet in the buffer at the specified offset. setAtOffset :: Octet -> Offset -> buf -> m Unit + -- | Creates a new buffer slice that acts like a window on the original buffer. + -- | Writing to the slice buffer updates the original buffer and vice-versa. + slice :: Offset -> Offset -> buf -> buf + -- | Returns the size of a buffer. size :: buf -> m Int @@ -135,6 +140,7 @@ instance mutableBufferEffect :: MutableBuffer EffectBuffer Effect where toArray = toArrayImpl getAtOffset = getAtOffsetImpl setAtOffset = setAtOffsetImpl + slice = sliceImpl size = sizeImpl concat = concatImpl concat' = concatImpl' @@ -157,6 +163,7 @@ instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where toArray = toArrayImpl getAtOffset = getAtOffsetImpl setAtOffset = setAtOffsetImpl + slice = sliceImpl size = sizeImpl concat = concatImpl concat' = concatImpl' @@ -214,6 +221,9 @@ getAtOffsetImpl o = usingFromFrozen $ Buffer.getAtOffset o foreign import setAtOffsetImpl :: forall buf m. Octet -> Offset -> buf -> m Unit +sliceImpl :: forall buf. Offset -> Offset -> buf -> buf +sliceImpl = unsafeCoerce Buffer.slice + sizeImpl :: forall buf m. buf -> m Int sizeImpl = usingFromFrozen Buffer.size diff --git a/src/Node/Buffer/Mutable/Unsafe.purs b/src/Node/Buffer/Mutable/Unsafe.purs deleted file mode 100644 index 552d4b9..0000000 --- a/src/Node/Buffer/Mutable/Unsafe.purs +++ /dev/null @@ -1,30 +0,0 @@ --- | Unsafe operations on mutable buffers. -module Node.Buffer.Mutable.Unsafe - ( class MutableBufferUnsafe - , slice - ) where - -import Effect (Effect) -import Control.Monad.ST (ST, kind Region) -import Node.Buffer as Buffer -import Node.Buffer (Offset) -import Node.Buffer.Mutable (class MutableBuffer, EffectBuffer, STBuffer) -import Unsafe.Coerce (unsafeCoerce) - -class MutableBuffer buf m <= MutableBufferUnsafe buf m | buf -> m, m -> buf where - - -- | Creates a new buffer slice that acts like a window on the original buffer. - -- | Writing to the slice buffer updates the original buffer and vice-versa. - -- | - -- | This is considered unsafe as writing to a slice can result in action at a - -- | distance. - slice :: Offset -> Offset -> buf -> buf - -instance mutableBufferUnsafeEffect :: MutableBufferUnsafe EffectBuffer Effect where - slice = sliceImpl - -instance mutableBufferUnsafeST :: MutableBufferUnsafe (STBuffer h) (ST h) where - slice = sliceImpl - -sliceImpl :: forall buf. Offset -> Offset -> buf -> buf -sliceImpl = unsafeCoerce Buffer.slice diff --git a/test/Test/Node/Buffer/Mutable.purs b/test/Test/Node/Buffer/Mutable.purs index f8d5ae2..3f5c6d0 100644 --- a/test/Test/Node/Buffer/Mutable.purs +++ b/test/Test/Node/Buffer/Mutable.purs @@ -9,10 +9,9 @@ import Effect (Effect) import Effect.Console (log) import Node.Buffer (BufferValueType(..)) import Node.Buffer as Buffer -import Node.Buffer.Mutable (class MutableBuffer, EffectBuffer, STBuffer, concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, toArrayBuffer, fromString, getAtOffset, read, readString, runST, thaw, toArray, toString, write) +import Node.Buffer.Mutable (class MutableBuffer, EffectBuffer, STBuffer, concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, toArrayBuffer, fromString, getAtOffset, setAtOffset, read, readString, runST, thaw, toArray, toString, write, slice) import Node.Encoding (Encoding(..)) import Test.Assert (assertEqual) -import Test.Node.Buffer.Mutable.Unsafe as Unsafe import Type.Proxy (Proxy(..)) import Unsafe.Coerce (unsafeCoerce) @@ -27,8 +26,6 @@ test = do log " - runST" testRunSt - Unsafe.test - testMutableBuffer :: forall buf m. MutableBuffer buf m => Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit testMutableBuffer _ run = do @@ -63,6 +60,9 @@ testMutableBuffer _ run = do log " - readString" testReadString + log " - slice" + testSlice + log " - copy" testCopy @@ -154,6 +154,19 @@ testMutableBuffer _ run = do assertEqual {expected: "world", actual: strOut} + testSlice :: Effect Unit + testSlice = do + {bufArr, bufSliceArr} <- run do + buf <- fromArray [1, 2, 3, 4] :: m buf + let bufSlice = slice 1 3 buf :: buf + setAtOffset 42 1 bufSlice + bufArr <- toArray buf + bufSliceArr <- toArray bufSlice + pure {bufArr, bufSliceArr} + + assertEqual {expected: [1, 2, 42, 4], actual: bufArr} + assertEqual {expected: [2, 42], actual: bufSliceArr} + testCopy :: Effect Unit testCopy = do {copied, out} <- run do diff --git a/test/Test/Node/Buffer/Mutable/Unsafe.purs b/test/Test/Node/Buffer/Mutable/Unsafe.purs deleted file mode 100644 index 32ccdd2..0000000 --- a/test/Test/Node/Buffer/Mutable/Unsafe.purs +++ /dev/null @@ -1,41 +0,0 @@ -module Test.Node.Buffer.Mutable.Unsafe (test) where - -import Prelude - -import Effect (Effect) -import Effect.Console (log) -import Control.Monad.ST as ST -import Node.Buffer.Mutable (EffectBuffer, STBuffer, fromArray, setAtOffset, toArray) -import Node.Buffer.Mutable.Unsafe (class MutableBufferUnsafe, slice) -import Test.Assert (assertEqual) -import Type.Proxy (Proxy(..)) -import Unsafe.Coerce (unsafeCoerce) - -test :: Effect Unit -test = do - log "Testing Node.Buffer.Mutable.Unsafe [EffectBuffer] ..." - testMutableBufferUnsafe (Proxy :: Proxy EffectBuffer) identity - - log "Testing Node.Buffer.Mutable.Unsafe [STBuffer] ..." - testMutableBufferUnsafe (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) - -testMutableBufferUnsafe :: forall buf m. MutableBufferUnsafe buf m => - Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit -testMutableBufferUnsafe _ run = do - - log " - slice" - testSlice - - where - testSlice :: Effect Unit - testSlice = do - {bufArr, bufSliceArr} <- run do - buf <- fromArray [1, 2, 3, 4] :: m buf - let bufSlice = slice 1 3 buf :: buf - setAtOffset 42 1 bufSlice - bufArr <- toArray buf - bufSliceArr <- toArray bufSlice - pure {bufArr, bufSliceArr} - - assertEqual {expected: [1, 2, 42, 4], actual: bufArr} - assertEqual {expected: [2, 42], actual: bufSliceArr} From fec1805dcca65c4aea48ed18ca4bdc8d7f16c8c6 Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sat, 5 Jan 2019 14:30:39 +0000 Subject: [PATCH 05/11] Add unsafeFreeze and unsafeThaw methods to MutableBuffer. As requested in #discussion_r244536918 --- src/Node/Buffer/Mutable.purs | 60 ++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/Node/Buffer/Mutable.purs b/src/Node/Buffer/Mutable.purs index d5717b6..933c723 100644 --- a/src/Node/Buffer/Mutable.purs +++ b/src/Node/Buffer/Mutable.purs @@ -2,7 +2,9 @@ module Node.Buffer.Mutable ( class MutableBuffer , create , freeze + , unsafeFreeze , thaw + , unsafeThaw , fromArray , fromString , fromArrayBuffer @@ -48,9 +50,17 @@ class Monad m <= MutableBuffer buf m | m -> buf, buf -> m where -- | Creates an immutable copy of a mutable buffer. freeze :: buf -> m Buffer +-- | O(1). Convert a mutable buffer to an immutable buffer, without copying. The +-- | mutable buffer must not be mutated afterwards. + unsafeFreeze :: buf -> m Buffer + -- | Creates a mutable copy of an immutable buffer. thaw :: Buffer -> m buf + -- | O(1) Convert an immutable buffer to a mutable buffer, without copying. The + -- | input buffer must not be used afterward. + unsafeThaw :: Buffer -> m buf + -- | Creates a new buffer from an array of octets, sized to match the array. fromArray :: Array Octet -> m buf @@ -121,13 +131,15 @@ foreign import data STBuffer :: Region -> Type -- | Runs an effect creating an `STBuffer` then freezes the buffer and returns -- | it, without unneccessary copying. -runST :: forall h. ST h (STBuffer h) -> Buffer -runST st = ST.run (unsafeCoerce st) +runST :: (forall h. ST h (STBuffer h)) -> Buffer +runST st = ST.run (st >>= unsafeFreeze) instance mutableBufferEffect :: MutableBuffer EffectBuffer Effect where create = createImpl freeze = copyAllImpl + unsafeFreeze = unsafeFreezeImpl thaw = copyAllImpl + unsafeThaw = unsafeThawImpl fromArray = fromArrayImpl fromString = fromStringImpl fromArrayBuffer = fromArrayBufferImpl @@ -150,7 +162,9 @@ instance mutableBufferEffect :: MutableBuffer EffectBuffer Effect where instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where create = createImpl freeze = copyAllImpl + unsafeFreeze = unsafeFreezeImpl thaw = copyAllImpl + unsafeThaw = unsafeThawImpl fromArray = fromArrayImpl fromString = fromStringImpl fromArrayBuffer = fromArrayBufferImpl @@ -170,53 +184,59 @@ instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where copy = copyImpl fill = fillImpl -usingFromFrozen :: forall buf m a. (Buffer -> a) -> buf -> m a -usingFromFrozen f buf = unsafeCoerce \_ -> f $ unsafeCoerce buf +unsafeFreezeImpl :: forall buf m. Monad m => buf -> m Buffer +unsafeFreezeImpl = pure <<< unsafeCoerce + +unsafeThawImpl :: forall buf m. Monad m => Buffer -> m buf +unsafeThawImpl = pure <<< unsafeCoerce + +usingFromFrozen :: forall buf m a. Monad m => (Buffer -> a) -> buf -> m a +usingFromFrozen f buf = f <$> unsafeFreezeImpl buf -usingToFrozen :: forall buf m a. (a -> Buffer) -> a -> m buf -usingToFrozen f x = unsafeCoerce \_ -> unsafeCoerce $ f x +usingToFrozen :: forall buf m a. Monad m => (a -> Buffer) -> a -> m buf +usingToFrozen f x = unsafeThawImpl $ f x -createImpl :: forall buf m. Int -> m buf +createImpl :: forall buf m. Monad m => Int -> m buf createImpl = usingToFrozen Buffer.create foreign import copyAllImpl :: forall a buf m. a -> m buf -fromArrayImpl :: forall buf m. Array Octet -> m buf +fromArrayImpl :: forall buf m. Monad m => Array Octet -> m buf fromArrayImpl = usingToFrozen Buffer.fromArray -fromStringImpl :: forall buf m. String -> Encoding -> m buf +fromStringImpl :: forall buf m. Monad m => String -> Encoding -> m buf fromStringImpl s = usingToFrozen $ Buffer.fromString s -fromArrayBufferImpl :: forall buf m. ArrayBuffer -> m buf +fromArrayBufferImpl :: forall buf m. Monad m => ArrayBuffer -> m buf fromArrayBufferImpl = usingToFrozen Buffer.fromArrayBuffer -toArrayBufferImpl :: forall buf m. buf -> m ArrayBuffer +toArrayBufferImpl :: forall buf m. Monad m => buf -> m ArrayBuffer toArrayBufferImpl = usingFromFrozen Buffer.toArrayBuffer -readImpl :: forall buf m. BufferValueType -> Offset -> buf -> m Int +readImpl :: forall buf m. Monad m => BufferValueType -> Offset -> buf -> m Int readImpl t o = usingFromFrozen $ Buffer.read t o -readStringImpl :: forall buf m. Encoding -> Offset -> Offset -> buf -> m String +readStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Offset -> buf -> m String readStringImpl m o o' = usingFromFrozen $ Buffer.readString m o o' -toStringImpl :: forall buf m. Encoding -> buf -> m String +toStringImpl :: forall buf m. Monad m => Encoding -> buf -> m String toStringImpl m = usingFromFrozen $ Buffer.toString m -writeImpl :: forall buf m. BufferValueType -> Int -> Offset -> buf -> m Unit +writeImpl :: forall buf m. Monad m => BufferValueType -> Int -> Offset -> buf -> m Unit writeImpl = writeInternal <<< show foreign import writeInternal :: forall buf m. String -> Int -> Offset -> buf -> m Unit -writeStringImpl :: forall buf m. Encoding -> Offset -> Int -> String -> buf -> m Int +writeStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Int -> String -> buf -> m Int writeStringImpl = writeStringInternal <<< encodingToNode foreign import writeStringInternal :: forall buf m. String -> Offset -> Int -> String -> buf -> m Int -toArrayImpl :: forall buf m. buf -> m (Array Octet) +toArrayImpl :: forall buf m. Monad m => buf -> m (Array Octet) toArrayImpl = usingFromFrozen Buffer.toArray -getAtOffsetImpl :: forall buf m. Offset -> buf -> m (Maybe Octet) +getAtOffsetImpl :: forall buf m. Monad m => Offset -> buf -> m (Maybe Octet) getAtOffsetImpl o = usingFromFrozen $ Buffer.getAtOffset o foreign import setAtOffsetImpl :: forall buf m. Octet -> Offset -> buf -> m Unit @@ -224,13 +244,13 @@ foreign import setAtOffsetImpl :: forall buf m. Octet -> Offset -> buf -> m Unit sliceImpl :: forall buf. Offset -> Offset -> buf -> buf sliceImpl = unsafeCoerce Buffer.slice -sizeImpl :: forall buf m. buf -> m Int +sizeImpl :: forall buf m. Monad m => buf -> m Int sizeImpl = usingFromFrozen Buffer.size concatImpl :: forall buf m. Array buf -> m buf concatImpl arrs = unsafeCoerce \_ -> Buffer.concat (unsafeCoerce arrs) -concatImpl' :: forall buf m. Array buf -> Int -> m buf +concatImpl' :: forall buf m. Monad m => Array buf -> Int -> m buf concatImpl' arrs n = unsafeCoerce \_ -> Buffer.concat' (unsafeCoerce arrs) n foreign import copyImpl :: forall buf m. Offset -> Offset -> buf -> Offset -> buf -> m Int From a71708d2f6a8e6ff2b8f8379b3d11c386474e82a Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sun, 6 Jan 2019 14:49:58 +0000 Subject: [PATCH 06/11] Remove some unnecessary type assertions from the tests, and fix testFill --- test/Test/Node/Buffer/Mutable.purs | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/test/Test/Node/Buffer/Mutable.purs b/test/Test/Node/Buffer/Mutable.purs index 3f5c6d0..c6d5c68 100644 --- a/test/Test/Node/Buffer/Mutable.purs +++ b/test/Test/Node/Buffer/Mutable.purs @@ -78,24 +78,24 @@ testMutableBuffer _ run = do where testCreate :: Effect Unit testCreate = do - buf <- run ((create 3 :: m buf) >>= toArray) + buf <- run (create 3 >>= toArray) assertEqual {expected: [0, 0, 0], actual: buf} testFreeze :: Effect Unit testFreeze = do - buf <- Buffer.toArray <$> run ((fromArray [1, 2, 3] :: m buf) >>= freeze) + buf <- Buffer.toArray <$> run (fromArray [1, 2, 3] >>= freeze) assertEqual {expected: [1, 2, 3], actual: buf} testThaw :: Effect Unit testThaw = do - buf <- run ((thaw (Buffer.fromArray [1, 2, 3]) :: m buf) >>= toArray) + buf <- run (thaw (Buffer.fromArray [1, 2, 3]) >>= toArray) assertEqual {expected: [1, 2, 3], actual: buf} testReadWrite :: Effect Unit testReadWrite = do let val = 42 readVal <- run do - buf <- create 1 :: m buf + buf <- create 1 write UInt8 val 0 buf read UInt8 0 buf @@ -104,7 +104,7 @@ testMutableBuffer _ run = do testFromArray :: Effect Unit testFromArray = do readVal <- run do - buf <- fromArray [1,2,3,4,5] :: m buf + buf <- fromArray [1,2,3,4,5] read UInt8 2 buf assertEqual {expected: 3, actual: readVal} @@ -113,7 +113,7 @@ testMutableBuffer _ run = do testToArray = do let val = [1,2,67,3,3,7,8,3,4,237] valOut <- run do - buf <- fromArray val :: m buf + buf <- fromArray val toArray buf assertEqual {expected: val, actual: valOut} @@ -122,7 +122,7 @@ testMutableBuffer _ run = do testFromString = do let str = "hello, world" val <- run do - buf <- fromString str ASCII :: m buf + buf <- fromString str ASCII read UInt8 6 buf assertEqual {expected: 32, actual: val} -- ASCII space @@ -140,7 +140,7 @@ testMutableBuffer _ run = do testToString = do let str = "hello, world" strOut <-run do - buf <- fromString str ASCII :: m buf + buf <- fromString str ASCII toString ASCII buf assertEqual {expected: str, actual: strOut} @@ -149,7 +149,7 @@ testMutableBuffer _ run = do testReadString = do let str = "hello, world" strOut <- run do - buf <- fromString str ASCII :: m buf + buf <- fromString str ASCII readString ASCII 7 12 buf assertEqual {expected: "world", actual: strOut} @@ -157,8 +157,8 @@ testMutableBuffer _ run = do testSlice :: Effect Unit testSlice = do {bufArr, bufSliceArr} <- run do - buf <- fromArray [1, 2, 3, 4] :: m buf - let bufSlice = slice 1 3 buf :: buf + buf <- fromArray [1, 2, 3, 4] + let bufSlice = slice 1 3 buf setAtOffset 42 1 bufSlice bufArr <- toArray buf bufSliceArr <- toArray bufSlice @@ -170,7 +170,7 @@ testMutableBuffer _ run = do testCopy :: Effect Unit testCopy = do {copied, out} <- run do - buf1 <- fromArray [1,2,3,4,5] :: m buf + buf1 <- fromArray [1,2,3,4,5] buf2 <- fromArray [10,9,8,7,6] copied <- copy 0 3 buf1 2 buf2 out <- toArray buf2 @@ -181,10 +181,10 @@ testMutableBuffer _ run = do testFill :: Effect Unit testFill = do - let out = ST.run do - buf <- fromArray [1,1,1,1,1] - fill 42 2 4 buf - toArray buf + out <- run do + buf <- fromArray [1,1,1,1,1] + fill 42 2 4 buf + toArray buf assertEqual {expected: [1,1,42,42,1], actual: out} @@ -192,7 +192,7 @@ testMutableBuffer _ run = do testConcat' = do out <- run do bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] - buf <- concat' bufs 15 :: m buf + buf <- concat' bufs 15 toArray buf assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} @@ -200,7 +200,7 @@ testMutableBuffer _ run = do testGetAtOffset :: Effect Unit testGetAtOffset = do {o1, o4, om1} <- run do - buf <- fromArray [1, 2, 3, 4] :: m buf + buf <- fromArray [1, 2, 3, 4] o1 <- getAtOffset 1 buf o4 <- getAtOffset 4 buf om1 <- getAtOffset (-1) buf From 487e409e595b6f3069ed3e85101657edd9e5c9ae Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sun, 6 Jan 2019 16:02:00 +0000 Subject: [PATCH 07/11] Re-arrange the modules to keep backwards compatibility with master. Mutable buffers are now the default again, and immutable buffers are moved to Node.Buffer.Immutable --- src/Node/Buffer.js | 121 ++++------ src/Node/Buffer.purs | 323 ++++++++++++++++++--------- src/Node/Buffer/Immutable.js | 103 +++++++++ src/Node/Buffer/Immutable.purs | 109 +++++++++ src/Node/Buffer/Mutable.js | 74 ------ src/Node/Buffer/Mutable.purs | 258 --------------------- src/Node/Buffer/Types.purs | 47 ++++ test/Test/Main.purs | 4 +- test/Test/Node/Buffer.purs | 280 +++++++++++++++-------- test/Test/Node/Buffer/Immutable.purs | 130 +++++++++++ test/Test/Node/Buffer/Mutable.purs | 216 ------------------ 11 files changed, 837 insertions(+), 828 deletions(-) create mode 100644 src/Node/Buffer/Immutable.js create mode 100644 src/Node/Buffer/Immutable.purs delete mode 100644 src/Node/Buffer/Mutable.js delete mode 100644 src/Node/Buffer/Mutable.purs create mode 100644 src/Node/Buffer/Types.purs create mode 100644 test/Test/Node/Buffer/Immutable.purs delete mode 100644 test/Test/Node/Buffer/Mutable.purs diff --git a/src/Node/Buffer.js b/src/Node/Buffer.js index 3809ae9..95b7a5d 100644 --- a/src/Node/Buffer.js +++ b/src/Node/Buffer.js @@ -1,103 +1,74 @@ /* global exports */ /* global Buffer */ -/* global require */ "use strict"; -exports.showImpl = require('util').inspect; - -exports.eqImpl = function(a) { - return function(b) { - return a.equals(b); - } -}; - -exports.compareImpl = function(a) { - return function (b) { - return a.compare(b); +exports.copyAllImpl = function(a) { + return function() { + return Buffer.from(a); }; -} - -exports.create = function (size) { - return Buffer.alloc(size); -}; - -exports.fromArray = function (octets) { - return Buffer.from(octets); -}; - -exports.size = function (buff) { - return buff.length; -}; - -exports.toArray = function (buff) { - var json = buff.toJSON() - return json.data || json; -}; - -exports.toArrayBuffer = function(buff) { - return buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength); -}; - -exports.fromArrayBuffer = function(ab) { - return Buffer.from(ab); }; -exports.fromStringImpl = function (str) { - return function (encoding) { - return Buffer.from(str, encoding); +exports.writeInternal = function (ty) { + return function (value) { + return function (offset) { + return function (buf) { + return function() { + buf['write' + ty](value, offset); + return {}; + } + }; + }; }; }; -exports.readImpl = function (ty) { +exports.writeStringInternal = function (encoding) { return function (offset) { - return function (buf) { - return buf['read' + ty](offset); + return function (length) { + return function (value) { + return function (buff) { + return function() { + return buff.write(value, offset, length, encoding); + } + }; + }; }; }; }; -exports.readStringImpl = function (enc) { - return function (start) { - return function (end) { - return function (buff) { - return buff.toString(enc, start, end); +exports.setAtOffsetImpl = function (value) { + return function (offset) { + return function (buff) { + return function() { + buff[offset] = value; + return {}; }; }; }; }; -exports.getAtOffsetImpl = function (just) { - return function (nothing) { - return function (offset) { - return function (buff) { - var octet = buff[offset]; - return octet == null ? nothing - : just(octet); +exports.copyImpl = function (srcStart) { + return function (srcEnd) { + return function (src) { + return function (targStart) { + return function (targ) { + return function() { + return src.copy(targ, targStart, srcStart, srcEnd); + }; + }; }; }; }; }; -exports.toStringImpl = function (enc) { - return function (buff) { - return buff.toString(enc); - }; -}; - -exports.slice = function (start) { - return function (end) { - return function (buff) { - return buff.slice(start, end); +exports.fillImpl = function (octet) { + return function (start) { + return function (end) { + return function (buf) { + return function() { + buf.fill(octet, start, end); + return {}; + }; + }; }; }; }; - -exports.concat = function (buffs) { - return Buffer.concat(buffs); -}; - -exports["concat'"] = function (buffs) { - return function (totalLength) { - return Buffer.concat(buffs, totalLength); - }; -}; diff --git a/src/Node/Buffer.purs b/src/Node/Buffer.purs index 81ab805..7d0754f 100644 --- a/src/Node/Buffer.purs +++ b/src/Node/Buffer.purs @@ -1,151 +1,262 @@ -- | Immutable buffers and associated operations. module Node.Buffer - ( Octet - , Offset - , BufferValueType(..) - , Buffer + ( class MutableBuffer , create + , freeze + , unsafeFreeze + , thaw + , unsafeThaw , fromArray , fromString , fromArrayBuffer + , toArrayBuffer , read , readString , toString + , write + , writeString , toArray - , toArrayBuffer , getAtOffset - , concat - , concat' + , setAtOffset , slice , size + , concat + , concat' + , copy + , fill + , Buffer + , STBuffer + , runST + , module TypesExports ) where import Prelude +import Control.Monad.ST (ST, kind Region) +import Control.Monad.ST as ST import Data.ArrayBuffer.Types (ArrayBuffer) -import Data.Maybe (Maybe(..)) +import Data.Maybe (Maybe) +import Effect (Effect) +import Node.Buffer.Immutable (ImmutableBuffer) +import Node.Buffer.Immutable as Immutable +import Node.Buffer.Types (BufferValueType, Octet, Offset) +import Node.Buffer.Types (BufferValueType(..), Octet, Offset) as TypesExports import Node.Encoding (Encoding, encodingToNode) +import Unsafe.Coerce (unsafeCoerce) --- | Type synonym indicating the value should be an octet (0-255). If the value --- | provided is outside this range it will be used as modulo 256. -type Octet = Int - --- | Type synonym indicating the value refers to an offset in a buffer. -type Offset = Int - --- | Enumeration of the numeric types that can be written to a buffer. -data BufferValueType - = UInt8 - | UInt16LE - | UInt16BE - | UInt32LE - | UInt32BE - | Int8 - | Int16LE - | Int16BE - | Int32LE - | Int32BE - | FloatLE - | FloatBE - | DoubleLE - | DoubleBE - -instance showBufferValueType :: Show BufferValueType where - show UInt8 = "UInt8" - show UInt16LE = "UInt16LE" - show UInt16BE = "UInt16BE" - show UInt32LE = "UInt32LE" - show UInt32BE = "UInt32BE" - show Int8 = "Int8" - show Int16LE = "Int16LE" - show Int16BE = "Int16BE" - show Int32LE = "Int32LE" - show Int32BE = "Int32BE" - show FloatLE = "FloatLE" - show FloatBE = "FloatBE" - show DoubleLE = "DoubleLE" - show DoubleBE = "DoubleBE" - --- | An immutable buffer that exists independently of any memory region or effect. -foreign import data Buffer :: Type +-- | A type class for mutable buffers `buf` where operations on those buffers are +-- | represented by a particular monadic effect type `m`. +class Monad m <= MutableBuffer buf m | m -> buf, buf -> m where + + -- | Creates a new buffer of the specified size. + create :: Int -> m buf + + -- | Creates an immutable copy of a mutable buffer. + freeze :: buf -> m ImmutableBuffer -instance showBuffer :: Show Buffer where - show = showImpl + -- | O(1). Convert a mutable buffer to an immutable buffer, without copying. The + -- | mutable buffer must not be mutated afterwards. + unsafeFreeze :: buf -> m ImmutableBuffer -foreign import showImpl :: Buffer -> String + -- | Creates a mutable copy of an immutable buffer. + thaw :: ImmutableBuffer -> m buf -instance eqBuffer :: Eq Buffer where - eq = eqImpl + -- | O(1) Convert an immutable buffer to a mutable buffer, without copying. The + -- | input buffer must not be used afterward. + unsafeThaw :: ImmutableBuffer -> m buf -foreign import eqImpl :: Buffer -> Buffer -> Boolean + -- | Creates a new buffer from an array of octets, sized to match the array. + fromArray :: Array Octet -> m buf -instance ordBuffer :: Ord Buffer where - compare a b = - case compareImpl a b of - x | x < 0 -> LT - x | x > 0 -> GT - otherwise -> EQ + -- | Creates a new buffer from a string with the specified encoding, sized to + -- | match the string. + fromString :: String -> Encoding -> m buf -foreign import compareImpl :: Buffer -> Buffer -> Int + -- | Creates a buffer view from a JS ArrayByffer without copying data. + fromArrayBuffer :: ArrayBuffer -> m buf --- | Creates a new buffer of the specified size. -foreign import create :: Int -> Buffer + -- | Copies the data in the buffer to a new JS ArrayBuffer + toArrayBuffer :: buf -> m ArrayBuffer --- | Creates a new buffer from an array of octets, sized to match the array. -foreign import fromArray :: Array Octet -> Buffer + -- | Reads a numeric value from a buffer at the specified offset. + read :: BufferValueType -> Offset -> buf -> m Int --- | Creates a buffer view from a JS ArrayByffer without copying data. --- --- Requires Node >= v5.10.0 -foreign import fromArrayBuffer :: ArrayBuffer -> Buffer + -- | Reads a section of a buffer as a string with the specified encoding. + readString :: Encoding -> Offset -> Offset -> buf -> m String --- | Creates a new buffer from a string with the specified encoding, sized to match the string. -fromString :: String -> Encoding -> Buffer -fromString str = fromStringImpl str <<< encodingToNode + -- | Reads the buffer as a string with the specified encoding. + toString :: Encoding -> buf -> m String -foreign import fromStringImpl :: String -> String -> Buffer + -- | Writes a numeric value to a buffer at the specified offset. + write :: BufferValueType -> Int -> Offset -> buf -> m Unit --- | Reads a numeric value from a buffer at the specified offset. -read :: BufferValueType -> Offset -> Buffer -> Int -read = readImpl <<< show + -- | Writes octets from a string to a buffer at the specified offset. Multi-byte + -- | characters will not be written to the buffer if there is not enough capacity + -- | to write them fully. The number of bytes written is returned. + writeString :: Encoding -> Offset -> Int -> String -> buf -> m Int -foreign import readImpl :: String -> Offset -> Buffer -> Int + -- | Creates an array of octets from a buffer's contents. + toArray :: buf -> m (Array Octet) --- | Reads a section of a buffer as a string with the specified encoding. -readString :: Encoding -> Offset -> Offset -> Buffer -> String -readString = readStringImpl <<< encodingToNode + -- | Reads an octet from a buffer at the specified offset. + getAtOffset :: Offset -> buf -> m (Maybe Octet) -foreign import readStringImpl :: - String -> Offset -> Offset -> Buffer -> String + -- | Writes an octet in the buffer at the specified offset. + setAtOffset :: Octet -> Offset -> buf -> m Unit --- | Reads the buffer as a string with the specified encoding. -toString :: Encoding -> Buffer -> String -toString = toStringImpl <<< encodingToNode + -- | Creates a new buffer slice that acts like a window on the original buffer. + -- | Writing to the slice buffer updates the original buffer and vice-versa. + slice :: Offset -> Offset -> buf -> buf -foreign import toStringImpl :: String -> Buffer -> String + -- | Returns the size of a buffer. + size :: buf -> m Int --- | Creates an array of octets from a buffer's contents. -foreign import toArray :: Buffer -> Array Octet + -- | Concatenates a list of buffers. + concat :: Array buf -> m buf --- | Creates an `ArrayBuffer` by copying a buffer's contents. -foreign import toArrayBuffer :: Buffer -> ArrayBuffer + -- | Concatenates a list of buffers, combining them into a new buffer of the + -- | specified length. + concat' :: Array buf -> Int -> m buf --- | Reads an octet from a buffer at the specified offset. -getAtOffset :: Offset -> Buffer -> Maybe Octet -getAtOffset = getAtOffsetImpl Just Nothing + -- | Copies a section of a source buffer into a target buffer at the specified + -- | offset, and returns the number of octets copied. + copy :: Offset -> Offset -> buf -> Offset -> buf -> m Int + + -- | Fills a range in a buffer with the specified octet. + fill :: Octet -> Offset -> Offset -> buf -> m Unit + +-- | A reference to a mutable buffer for use with `Effect` +foreign import data Buffer :: Type -foreign import getAtOffsetImpl :: - (Octet -> Maybe Octet) -> Maybe Octet -> Offset -> Buffer -> Maybe Octet +-- | A reference to a mutable buffer for use with `ST` +-- | +-- | The type parameter represents the memory region which the buffer belongs to. +foreign import data STBuffer :: Region -> Type + +-- | Runs an effect creating an `STBuffer` then freezes the buffer and returns +-- | it, without unneccessary copying. +runST :: (forall h. ST h (STBuffer h)) -> ImmutableBuffer +runST st = ST.run (st >>= unsafeFreeze) + +instance mutableBufferEffect :: MutableBuffer Buffer Effect where + create = createImpl + freeze = copyAllImpl + unsafeFreeze = unsafeFreezeImpl + thaw = copyAllImpl + unsafeThaw = unsafeThawImpl + fromArray = fromArrayImpl + fromString = fromStringImpl + fromArrayBuffer = fromArrayBufferImpl + toArrayBuffer = toArrayBufferImpl + read = readImpl + readString = readStringImpl + toString = toStringImpl + write = writeImpl + writeString = writeStringImpl + toArray = toArrayImpl + getAtOffset = getAtOffsetImpl + setAtOffset = setAtOffsetImpl + slice = sliceImpl + size = sizeImpl + concat = concatImpl + concat' = concatImpl' + copy = copyImpl + fill = fillImpl + +instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where + create = createImpl + freeze = copyAllImpl + unsafeFreeze = unsafeFreezeImpl + thaw = copyAllImpl + unsafeThaw = unsafeThawImpl + fromArray = fromArrayImpl + fromString = fromStringImpl + fromArrayBuffer = fromArrayBufferImpl + toArrayBuffer = toArrayBufferImpl + read = readImpl + readString = readStringImpl + toString = toStringImpl + write = writeImpl + writeString = writeStringImpl + toArray = toArrayImpl + getAtOffset = getAtOffsetImpl + setAtOffset = setAtOffsetImpl + slice = sliceImpl + size = sizeImpl + concat = concatImpl + concat' = concatImpl' + copy = copyImpl + fill = fillImpl + +unsafeFreezeImpl :: forall buf m. Monad m => buf -> m ImmutableBuffer +unsafeFreezeImpl = pure <<< unsafeCoerce + +unsafeThawImpl :: forall buf m. Monad m => ImmutableBuffer -> m buf +unsafeThawImpl = pure <<< unsafeCoerce + +usingFromFrozen :: forall buf m a. Monad m => (ImmutableBuffer -> a) -> buf -> m a +usingFromFrozen f buf = f <$> unsafeFreezeImpl buf + +usingToFrozen :: forall buf m a. Monad m => (a -> ImmutableBuffer) -> a -> m buf +usingToFrozen f x = unsafeThawImpl $ f x + +createImpl :: forall buf m. Monad m => Int -> m buf +createImpl = usingToFrozen Immutable.create + +foreign import copyAllImpl :: forall a buf m. a -> m buf + +fromArrayImpl :: forall buf m. Monad m => Array Octet -> m buf +fromArrayImpl = usingToFrozen Immutable.fromArray + +fromStringImpl :: forall buf m. Monad m => String -> Encoding -> m buf +fromStringImpl s = usingToFrozen $ Immutable.fromString s + +fromArrayBufferImpl :: forall buf m. Monad m => ArrayBuffer -> m buf +fromArrayBufferImpl = usingToFrozen Immutable.fromArrayBuffer + +toArrayBufferImpl :: forall buf m. Monad m => buf -> m ArrayBuffer +toArrayBufferImpl = usingFromFrozen Immutable.toArrayBuffer + +readImpl :: forall buf m. Monad m => BufferValueType -> Offset -> buf -> m Int +readImpl t o = usingFromFrozen $ Immutable.read t o + +readStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Offset -> buf -> m String +readStringImpl m o o' = usingFromFrozen $ Immutable.readString m o o' + +toStringImpl :: forall buf m. Monad m => Encoding -> buf -> m String +toStringImpl m = usingFromFrozen $ Immutable.toString m + +writeImpl :: forall buf m. Monad m => BufferValueType -> Int -> Offset -> buf -> m Unit +writeImpl = writeInternal <<< show + +foreign import writeInternal :: forall buf m. String -> Int -> Offset -> buf -> m Unit + +writeStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Int -> String -> buf -> m Int +writeStringImpl = writeStringInternal <<< encodingToNode + +foreign import writeStringInternal :: + forall buf m. String -> Offset -> Int -> String -> buf -> m Int + +toArrayImpl :: forall buf m. Monad m => buf -> m (Array Octet) +toArrayImpl = usingFromFrozen Immutable.toArray + +getAtOffsetImpl :: forall buf m. Monad m => Offset -> buf -> m (Maybe Octet) +getAtOffsetImpl o = usingFromFrozen $ Immutable.getAtOffset o + +foreign import setAtOffsetImpl :: forall buf m. Octet -> Offset -> buf -> m Unit + +sliceImpl :: forall buf. Offset -> Offset -> buf -> buf +sliceImpl = unsafeCoerce Immutable.slice + +sizeImpl :: forall buf m. Monad m => buf -> m Int +sizeImpl = usingFromFrozen Immutable.size --- | Concatenates a list of buffers. -foreign import concat :: Array Buffer -> Buffer +concatImpl :: forall buf m. Array buf -> m buf +concatImpl arrs = unsafeCoerce \_ -> Immutable.concat (unsafeCoerce arrs) --- | Concatenates a list of buffers, combining them into a new buffer of the --- | specified length. -foreign import concat' :: Array Buffer -> Int -> Buffer +concatImpl' :: forall buf m. Monad m => Array buf -> Int -> m buf +concatImpl' arrs n = unsafeCoerce \_ -> Immutable.concat' (unsafeCoerce arrs) n --- | Creates a new buffer slice that shares the memory of the original buffer. -foreign import slice :: Offset -> Offset -> Buffer -> Buffer +foreign import copyImpl :: forall buf m. Offset -> Offset -> buf -> Offset -> buf -> m Int --- | Returns the size of a buffer. -foreign import size :: Buffer -> Int +foreign import fillImpl :: forall buf m. Octet -> Offset -> Offset -> buf -> m Unit diff --git a/src/Node/Buffer/Immutable.js b/src/Node/Buffer/Immutable.js new file mode 100644 index 0000000..3809ae9 --- /dev/null +++ b/src/Node/Buffer/Immutable.js @@ -0,0 +1,103 @@ +/* global exports */ +/* global Buffer */ +/* global require */ +"use strict"; + +exports.showImpl = require('util').inspect; + +exports.eqImpl = function(a) { + return function(b) { + return a.equals(b); + } +}; + +exports.compareImpl = function(a) { + return function (b) { + return a.compare(b); + }; +} + +exports.create = function (size) { + return Buffer.alloc(size); +}; + +exports.fromArray = function (octets) { + return Buffer.from(octets); +}; + +exports.size = function (buff) { + return buff.length; +}; + +exports.toArray = function (buff) { + var json = buff.toJSON() + return json.data || json; +}; + +exports.toArrayBuffer = function(buff) { + return buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength); +}; + +exports.fromArrayBuffer = function(ab) { + return Buffer.from(ab); +}; + +exports.fromStringImpl = function (str) { + return function (encoding) { + return Buffer.from(str, encoding); + }; +}; + +exports.readImpl = function (ty) { + return function (offset) { + return function (buf) { + return buf['read' + ty](offset); + }; + }; +}; + +exports.readStringImpl = function (enc) { + return function (start) { + return function (end) { + return function (buff) { + return buff.toString(enc, start, end); + }; + }; + }; +}; + +exports.getAtOffsetImpl = function (just) { + return function (nothing) { + return function (offset) { + return function (buff) { + var octet = buff[offset]; + return octet == null ? nothing + : just(octet); + }; + }; + }; +}; + +exports.toStringImpl = function (enc) { + return function (buff) { + return buff.toString(enc); + }; +}; + +exports.slice = function (start) { + return function (end) { + return function (buff) { + return buff.slice(start, end); + }; + }; +}; + +exports.concat = function (buffs) { + return Buffer.concat(buffs); +}; + +exports["concat'"] = function (buffs) { + return function (totalLength) { + return Buffer.concat(buffs, totalLength); + }; +}; diff --git a/src/Node/Buffer/Immutable.purs b/src/Node/Buffer/Immutable.purs new file mode 100644 index 0000000..5994e1c --- /dev/null +++ b/src/Node/Buffer/Immutable.purs @@ -0,0 +1,109 @@ +-- | Immutable buffers and associated operations. +module Node.Buffer.Immutable + ( ImmutableBuffer + , create + , fromArray + , fromString + , fromArrayBuffer + , read + , readString + , toString + , toArray + , toArrayBuffer + , getAtOffset + , concat + , concat' + , slice + , size + ) where + +import Prelude + +import Data.ArrayBuffer.Types (ArrayBuffer) +import Data.Maybe (Maybe(..)) +import Node.Buffer.Types (BufferValueType, Octet, Offset) +import Node.Encoding (Encoding, encodingToNode) + +-- | An immutable buffer that exists independently of any memory region or effect. +foreign import data ImmutableBuffer :: Type + +instance showBuffer :: Show ImmutableBuffer where + show = showImpl + +foreign import showImpl :: ImmutableBuffer -> String + +instance eqBuffer :: Eq ImmutableBuffer where + eq = eqImpl + +foreign import eqImpl :: ImmutableBuffer -> ImmutableBuffer -> Boolean + +instance ordBuffer :: Ord ImmutableBuffer where + compare a b = + case compareImpl a b of + x | x < 0 -> LT + x | x > 0 -> GT + otherwise -> EQ + +foreign import compareImpl :: ImmutableBuffer -> ImmutableBuffer -> Int + +-- | Creates a new buffer of the specified size. +foreign import create :: Int -> ImmutableBuffer + +-- | Creates a new buffer from an array of octets, sized to match the array. +foreign import fromArray :: Array Octet -> ImmutableBuffer + +-- | Creates a buffer view from a JS ArrayByffer without copying data. +-- +-- Requires Node >= v5.10.0 +foreign import fromArrayBuffer :: ArrayBuffer -> ImmutableBuffer + +-- | Creates a new buffer from a string with the specified encoding, sized to match the string. +fromString :: String -> Encoding -> ImmutableBuffer +fromString str = fromStringImpl str <<< encodingToNode + +foreign import fromStringImpl :: String -> String -> ImmutableBuffer + +-- | Reads a numeric value from a buffer at the specified offset. +read :: BufferValueType -> Offset -> ImmutableBuffer -> Int +read = readImpl <<< show + +foreign import readImpl :: String -> Offset -> ImmutableBuffer -> Int + +-- | Reads a section of a buffer as a string with the specified encoding. +readString :: Encoding -> Offset -> Offset -> ImmutableBuffer -> String +readString = readStringImpl <<< encodingToNode + +foreign import readStringImpl :: + String -> Offset -> Offset -> ImmutableBuffer -> String + +-- | Reads the buffer as a string with the specified encoding. +toString :: Encoding -> ImmutableBuffer -> String +toString = toStringImpl <<< encodingToNode + +foreign import toStringImpl :: String -> ImmutableBuffer -> String + +-- | Creates an array of octets from a buffer's contents. +foreign import toArray :: ImmutableBuffer -> Array Octet + +-- | Creates an `ArrayBuffer` by copying a buffer's contents. +foreign import toArrayBuffer :: ImmutableBuffer -> ArrayBuffer + +-- | Reads an octet from a buffer at the specified offset. +getAtOffset :: Offset -> ImmutableBuffer -> Maybe Octet +getAtOffset = getAtOffsetImpl Just Nothing + +foreign import getAtOffsetImpl :: + (Octet -> Maybe Octet) -> Maybe Octet -> Offset -> ImmutableBuffer -> Maybe Octet + +-- | Concatenates a list of buffers. +foreign import concat :: Array ImmutableBuffer -> ImmutableBuffer + +-- | Concatenates a list of buffers, combining them into a new buffer of the +-- | specified length. +foreign import concat' :: Array ImmutableBuffer -> Int -> ImmutableBuffer + +-- | Creates a new buffer slice that shares the memory of the original buffer. +foreign import slice :: Offset -> Offset -> ImmutableBuffer -> ImmutableBuffer + +-- | Returns the size of a buffer. +foreign import size :: ImmutableBuffer -> Int diff --git a/src/Node/Buffer/Mutable.js b/src/Node/Buffer/Mutable.js deleted file mode 100644 index 95b7a5d..0000000 --- a/src/Node/Buffer/Mutable.js +++ /dev/null @@ -1,74 +0,0 @@ -/* global exports */ -/* global Buffer */ -"use strict"; - -exports.copyAllImpl = function(a) { - return function() { - return Buffer.from(a); - }; -}; - -exports.writeInternal = function (ty) { - return function (value) { - return function (offset) { - return function (buf) { - return function() { - buf['write' + ty](value, offset); - return {}; - } - }; - }; - }; -}; - -exports.writeStringInternal = function (encoding) { - return function (offset) { - return function (length) { - return function (value) { - return function (buff) { - return function() { - return buff.write(value, offset, length, encoding); - } - }; - }; - }; - }; -}; - -exports.setAtOffsetImpl = function (value) { - return function (offset) { - return function (buff) { - return function() { - buff[offset] = value; - return {}; - }; - }; - }; -}; - -exports.copyImpl = function (srcStart) { - return function (srcEnd) { - return function (src) { - return function (targStart) { - return function (targ) { - return function() { - return src.copy(targ, targStart, srcStart, srcEnd); - }; - }; - }; - }; - }; -}; - -exports.fillImpl = function (octet) { - return function (start) { - return function (end) { - return function (buf) { - return function() { - buf.fill(octet, start, end); - return {}; - }; - }; - }; - }; -}; diff --git a/src/Node/Buffer/Mutable.purs b/src/Node/Buffer/Mutable.purs deleted file mode 100644 index 933c723..0000000 --- a/src/Node/Buffer/Mutable.purs +++ /dev/null @@ -1,258 +0,0 @@ -module Node.Buffer.Mutable - ( class MutableBuffer - , create - , freeze - , unsafeFreeze - , thaw - , unsafeThaw - , fromArray - , fromString - , fromArrayBuffer - , toArrayBuffer - , read - , readString - , toString - , write - , writeString - , toArray - , getAtOffset - , setAtOffset - , slice - , size - , concat - , concat' - , copy - , fill - , EffectBuffer - , STBuffer - , runST - ) where - -import Prelude - -import Control.Monad.ST (ST, kind Region) -import Control.Monad.ST as ST -import Data.ArrayBuffer.Types (ArrayBuffer) -import Data.Maybe (Maybe) -import Effect (Effect) -import Node.Buffer (Buffer, BufferValueType, Octet, Offset) -import Node.Buffer as Buffer -import Node.Encoding (Encoding, encodingToNode) -import Unsafe.Coerce (unsafeCoerce) - --- | A type class for mutable buffers `buf` where operations on those buffers are --- | represented by a particular monadic effect type `m`. -class Monad m <= MutableBuffer buf m | m -> buf, buf -> m where - - -- | Creates a new buffer of the specified size. - create :: Int -> m buf - - -- | Creates an immutable copy of a mutable buffer. - freeze :: buf -> m Buffer - --- | O(1). Convert a mutable buffer to an immutable buffer, without copying. The --- | mutable buffer must not be mutated afterwards. - unsafeFreeze :: buf -> m Buffer - - -- | Creates a mutable copy of an immutable buffer. - thaw :: Buffer -> m buf - - -- | O(1) Convert an immutable buffer to a mutable buffer, without copying. The - -- | input buffer must not be used afterward. - unsafeThaw :: Buffer -> m buf - - -- | Creates a new buffer from an array of octets, sized to match the array. - fromArray :: Array Octet -> m buf - - -- | Creates a new buffer from a string with the specified encoding, sized to - -- | match the string. - fromString :: String -> Encoding -> m buf - - -- | Creates a buffer view from a JS ArrayByffer without copying data. - fromArrayBuffer :: ArrayBuffer -> m buf - - -- | Copies the data in the buffer to a new JS ArrayBuffer - toArrayBuffer :: buf -> m ArrayBuffer - - -- | Reads a numeric value from a buffer at the specified offset. - read :: BufferValueType -> Offset -> buf -> m Int - - -- | Reads a section of a buffer as a string with the specified encoding. - readString :: Encoding -> Offset -> Offset -> buf -> m String - - -- | Reads the buffer as a string with the specified encoding. - toString :: Encoding -> buf -> m String - - -- | Writes a numeric value to a buffer at the specified offset. - write :: BufferValueType -> Int -> Offset -> buf -> m Unit - - -- | Writes octets from a string to a buffer at the specified offset. Multi-byte - -- | characters will not be written to the buffer if there is not enough capacity - -- | to write them fully. The number of bytes written is returned. - writeString :: Encoding -> Offset -> Int -> String -> buf -> m Int - - -- | Creates an array of octets from a buffer's contents. - toArray :: buf -> m (Array Octet) - - -- | Reads an octet from a buffer at the specified offset. - getAtOffset :: Offset -> buf -> m (Maybe Octet) - - -- | Writes an octet in the buffer at the specified offset. - setAtOffset :: Octet -> Offset -> buf -> m Unit - - -- | Creates a new buffer slice that acts like a window on the original buffer. - -- | Writing to the slice buffer updates the original buffer and vice-versa. - slice :: Offset -> Offset -> buf -> buf - - -- | Returns the size of a buffer. - size :: buf -> m Int - - -- | Concatenates a list of buffers. - concat :: Array buf -> m buf - - -- | Concatenates a list of buffers, combining them into a new buffer of the - -- | specified length. - concat' :: Array buf -> Int -> m buf - - -- | Copies a section of a source buffer into a target buffer at the specified - -- | offset, and returns the number of octets copied. - copy :: Offset -> Offset -> buf -> Offset -> buf -> m Int - - -- | Fills a range in a buffer with the specified octet. - fill :: Octet -> Offset -> Offset -> buf -> m Unit - --- | A reference to a mutable buffer for use with `Effect` -foreign import data EffectBuffer :: Type - --- | A reference to a mutable buffer for use with `ST` --- | --- | The type parameter represents the memory region which the buffer belongs to. -foreign import data STBuffer :: Region -> Type - --- | Runs an effect creating an `STBuffer` then freezes the buffer and returns --- | it, without unneccessary copying. -runST :: (forall h. ST h (STBuffer h)) -> Buffer -runST st = ST.run (st >>= unsafeFreeze) - -instance mutableBufferEffect :: MutableBuffer EffectBuffer Effect where - create = createImpl - freeze = copyAllImpl - unsafeFreeze = unsafeFreezeImpl - thaw = copyAllImpl - unsafeThaw = unsafeThawImpl - fromArray = fromArrayImpl - fromString = fromStringImpl - fromArrayBuffer = fromArrayBufferImpl - toArrayBuffer = toArrayBufferImpl - read = readImpl - readString = readStringImpl - toString = toStringImpl - write = writeImpl - writeString = writeStringImpl - toArray = toArrayImpl - getAtOffset = getAtOffsetImpl - setAtOffset = setAtOffsetImpl - slice = sliceImpl - size = sizeImpl - concat = concatImpl - concat' = concatImpl' - copy = copyImpl - fill = fillImpl - -instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where - create = createImpl - freeze = copyAllImpl - unsafeFreeze = unsafeFreezeImpl - thaw = copyAllImpl - unsafeThaw = unsafeThawImpl - fromArray = fromArrayImpl - fromString = fromStringImpl - fromArrayBuffer = fromArrayBufferImpl - toArrayBuffer = toArrayBufferImpl - read = readImpl - readString = readStringImpl - toString = toStringImpl - write = writeImpl - writeString = writeStringImpl - toArray = toArrayImpl - getAtOffset = getAtOffsetImpl - setAtOffset = setAtOffsetImpl - slice = sliceImpl - size = sizeImpl - concat = concatImpl - concat' = concatImpl' - copy = copyImpl - fill = fillImpl - -unsafeFreezeImpl :: forall buf m. Monad m => buf -> m Buffer -unsafeFreezeImpl = pure <<< unsafeCoerce - -unsafeThawImpl :: forall buf m. Monad m => Buffer -> m buf -unsafeThawImpl = pure <<< unsafeCoerce - -usingFromFrozen :: forall buf m a. Monad m => (Buffer -> a) -> buf -> m a -usingFromFrozen f buf = f <$> unsafeFreezeImpl buf - -usingToFrozen :: forall buf m a. Monad m => (a -> Buffer) -> a -> m buf -usingToFrozen f x = unsafeThawImpl $ f x - -createImpl :: forall buf m. Monad m => Int -> m buf -createImpl = usingToFrozen Buffer.create - -foreign import copyAllImpl :: forall a buf m. a -> m buf - -fromArrayImpl :: forall buf m. Monad m => Array Octet -> m buf -fromArrayImpl = usingToFrozen Buffer.fromArray - -fromStringImpl :: forall buf m. Monad m => String -> Encoding -> m buf -fromStringImpl s = usingToFrozen $ Buffer.fromString s - -fromArrayBufferImpl :: forall buf m. Monad m => ArrayBuffer -> m buf -fromArrayBufferImpl = usingToFrozen Buffer.fromArrayBuffer - -toArrayBufferImpl :: forall buf m. Monad m => buf -> m ArrayBuffer -toArrayBufferImpl = usingFromFrozen Buffer.toArrayBuffer - -readImpl :: forall buf m. Monad m => BufferValueType -> Offset -> buf -> m Int -readImpl t o = usingFromFrozen $ Buffer.read t o - -readStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Offset -> buf -> m String -readStringImpl m o o' = usingFromFrozen $ Buffer.readString m o o' - -toStringImpl :: forall buf m. Monad m => Encoding -> buf -> m String -toStringImpl m = usingFromFrozen $ Buffer.toString m - -writeImpl :: forall buf m. Monad m => BufferValueType -> Int -> Offset -> buf -> m Unit -writeImpl = writeInternal <<< show - -foreign import writeInternal :: forall buf m. String -> Int -> Offset -> buf -> m Unit - -writeStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Int -> String -> buf -> m Int -writeStringImpl = writeStringInternal <<< encodingToNode - -foreign import writeStringInternal :: - forall buf m. String -> Offset -> Int -> String -> buf -> m Int - -toArrayImpl :: forall buf m. Monad m => buf -> m (Array Octet) -toArrayImpl = usingFromFrozen Buffer.toArray - -getAtOffsetImpl :: forall buf m. Monad m => Offset -> buf -> m (Maybe Octet) -getAtOffsetImpl o = usingFromFrozen $ Buffer.getAtOffset o - -foreign import setAtOffsetImpl :: forall buf m. Octet -> Offset -> buf -> m Unit - -sliceImpl :: forall buf. Offset -> Offset -> buf -> buf -sliceImpl = unsafeCoerce Buffer.slice - -sizeImpl :: forall buf m. Monad m => buf -> m Int -sizeImpl = usingFromFrozen Buffer.size - -concatImpl :: forall buf m. Array buf -> m buf -concatImpl arrs = unsafeCoerce \_ -> Buffer.concat (unsafeCoerce arrs) - -concatImpl' :: forall buf m. Monad m => Array buf -> Int -> m buf -concatImpl' arrs n = unsafeCoerce \_ -> Buffer.concat' (unsafeCoerce arrs) n - -foreign import copyImpl :: forall buf m. Offset -> Offset -> buf -> Offset -> buf -> m Int - -foreign import fillImpl :: forall buf m. Octet -> Offset -> Offset -> buf -> m Unit diff --git a/src/Node/Buffer/Types.purs b/src/Node/Buffer/Types.purs new file mode 100644 index 0000000..c6b2489 --- /dev/null +++ b/src/Node/Buffer/Types.purs @@ -0,0 +1,47 @@ +module Node.Buffer.Types + ( Octet + , Offset + , BufferValueType(..) + ) where + +import Prelude + +-- | Type synonym indicating the value should be an octet (0-255). If the value +-- | provided is outside this range it will be used as modulo 256. +type Octet = Int + +-- | Type synonym indicating the value refers to an offset in a buffer. +type Offset = Int + +-- | Enumeration of the numeric types that can be written to a buffer. +data BufferValueType + = UInt8 + | UInt16LE + | UInt16BE + | UInt32LE + | UInt32BE + | Int8 + | Int16LE + | Int16BE + | Int32LE + | Int32BE + | FloatLE + | FloatBE + | DoubleLE + | DoubleBE + +instance showBufferValueType :: Show BufferValueType where + show UInt8 = "UInt8" + show UInt16LE = "UInt16LE" + show UInt16BE = "UInt16BE" + show UInt32LE = "UInt32LE" + show UInt32BE = "UInt32BE" + show Int8 = "Int8" + show Int16LE = "Int16LE" + show Int16BE = "Int16BE" + show Int32LE = "Int32LE" + show Int32BE = "Int32BE" + show FloatLE = "FloatLE" + show FloatBE = "FloatBE" + show DoubleLE = "DoubleLE" + show DoubleBE = "DoubleBE" diff --git a/test/Test/Main.purs b/test/Test/Main.purs index d8d68aa..ba40432 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -3,9 +3,9 @@ module Test.Main where import Prelude import Effect (Effect) import Test.Node.Buffer as Buffer -import Test.Node.Buffer.Mutable as Mutable +import Test.Node.Buffer.Immutable as Immutable main :: Effect Unit main = do Buffer.test - Mutable.test + Immutable.test diff --git a/test/Test/Node/Buffer.purs b/test/Test/Node/Buffer.purs index bc98116..21b848b 100644 --- a/test/Test/Node/Buffer.purs +++ b/test/Test/Node/Buffer.purs @@ -2,128 +2,214 @@ module Test.Node.Buffer (test) where import Prelude +import Control.Monad.ST as ST import Data.Maybe (Maybe(..)) +import Data.Traversable (traverse) import Effect (Effect) -import Effect.Class.Console (log) -import Node.Buffer (Buffer, BufferValueType(..)) -import Node.Buffer as Buffer +import Effect.Console (log) +import Node.Buffer (class MutableBuffer, Buffer, STBuffer, BufferValueType(..), concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, toArrayBuffer, fromString, getAtOffset, setAtOffset, read, readString, runST, thaw, toArray, toString, write, slice) +import Node.Buffer.Immutable as Immutable import Node.Encoding (Encoding(..)) -import Test.Assert (assertEqual, assertTrue) +import Test.Assert (assertEqual) +import Type.Proxy (Proxy(..)) +import Unsafe.Coerce (unsafeCoerce) test :: Effect Unit test = do - log "Testing Node.Buffer ..." - log " - show" - testShow + log "Testing Node.Buffer [Effect] ..." + testBuffer (Proxy :: Proxy Buffer) identity - log " - eq" - testEq + log "Testing Node.Buffer [ST] ..." + testBuffer (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) + log " - runST" + testRunSt - log " - compare" - testCompare +testBuffer :: forall buf m. MutableBuffer buf m => + Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit +testBuffer _ run = do log " - create" testCreate + log " - freeze" + testFreeze + + log " - thaw" + testThaw + + log " - Reading and writing" + testReadWrite + + log " - fromArray" + testFromArray + + log " - toArray" + testToArray + log " - fromString" testFromString + log " - (to/from)ArrayBuffer" + testToFromArrayBuffer + log " - toString" testToString - log " - toArray" - testToArray - log " - readString" testReadString - log " - getAtOffset" - testGetAtOffset + log " - slice" + testSlice - log " - (to/from)ArrayBuffer" - testToFromArrayBuffer + log " - copy" + testCopy + + log " - fill" + testFill log " - concat'" testConcat' - log " - slice" - testSlice + log " - getAtOffset" + testGetAtOffset - log " - size" - testSize - -buffer123 :: Buffer -buffer123 = Buffer.fromArray [1, 2, 3] - -testShow :: Effect Unit -testShow = do - assertEqual {expected: "", actual: show buffer123} - -testEq :: Effect Unit -testEq = do - assertTrue $ buffer123 == buffer123 - assertTrue $ buffer123 == Buffer.fromArray [1, 2, 3] - assertTrue $ buffer123 /= Buffer.fromArray [1, 2, 4] - assertTrue $ buffer123 /= Buffer.fromArray [1, 2] - -testCompare :: Effect Unit -testCompare = do - assertEqual {expected: EQ, actual: compare buffer123 buffer123} - assertEqual {expected: LT, actual: compare buffer123 $ Buffer.fromArray [3, 2, 1]} - assertEqual {expected: GT, actual: compare buffer123 $ Buffer.fromArray [0, 1, 2]} - -testCreate :: Effect Unit -testCreate = do - assertEqual {expected: Buffer.fromArray [], actual: Buffer.create 0} - assertEqual {expected: Buffer.fromArray [0, 0, 0], actual: Buffer.create 3} - -testFromString :: Effect Unit -testFromString = do - let buf = Buffer.fromString "hello, world" ASCII - assertEqual {expected: 32, actual: Buffer.read UInt8 6 buf} - -testToString :: Effect Unit -testToString = do - let str = "hello, world" - str' = Buffer.toString ASCII $ Buffer.fromString str ASCII - assertEqual {expected: str, actual: str'} - -testToArray :: Effect Unit -testToArray = do - assertEqual {expected: [1, 2, 3], actual: Buffer.toArray buffer123} - -testReadString :: Effect Unit -testReadString = do - let str = "hello, world" - str' = Buffer.readString ASCII 7 12 $ Buffer.fromString str ASCII - assertEqual {expected: "world", actual: str'} - -testGetAtOffset :: Effect Unit -testGetAtOffset = do - assertEqual {expected: Just 2, actual: Buffer.getAtOffset 1 buffer123} - assertEqual {expected: Nothing, actual: Buffer.getAtOffset 99 buffer123} - assertEqual {expected: Nothing, actual: Buffer.getAtOffset (-1) buffer123} - -testToFromArrayBuffer :: Effect Unit -testToFromArrayBuffer = do - assertEqual {expected: buffer123, actual: Buffer.fromArrayBuffer $ Buffer.toArrayBuffer buffer123} - -testConcat' :: Effect Unit -testConcat' = do - let bufs = map (\x -> Buffer.fromArray [x, x+1, x+2]) [0,3,6,9,12] - buf = Buffer.concat' bufs 15 - out = Buffer.toArray buf - - assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} - -testSlice :: Effect Unit -testSlice = do - assertEqual {expected: buffer123, actual: Buffer.slice 0 3 buffer123} - assertEqual {expected: buffer123, actual: Buffer.slice 0 4 buffer123} - assertEqual {expected: Buffer.fromArray [2], actual: Buffer.slice 1 2 buffer123} - -testSize :: Effect Unit -testSize = do - assertEqual {expected: 0, actual: Buffer.size $ Buffer.fromArray []} - assertEqual {expected: 3, actual: Buffer.size buffer123} + where + testCreate :: Effect Unit + testCreate = do + buf <- run (create 3 >>= toArray) + assertEqual {expected: [0, 0, 0], actual: buf} + + testFreeze :: Effect Unit + testFreeze = do + buf <- Immutable.toArray <$> run (fromArray [1, 2, 3] >>= freeze) + assertEqual {expected: [1, 2, 3], actual: buf} + + testThaw :: Effect Unit + testThaw = do + buf <- run (thaw (Immutable.fromArray [1, 2, 3]) >>= toArray) + assertEqual {expected: [1, 2, 3], actual: buf} + + testReadWrite :: Effect Unit + testReadWrite = do + let val = 42 + readVal <- run do + buf <- create 1 + write UInt8 val 0 buf + read UInt8 0 buf + + assertEqual {expected: val, actual: readVal} + + testFromArray :: Effect Unit + testFromArray = do + readVal <- run do + buf <- fromArray [1,2,3,4,5] + read UInt8 2 buf + + assertEqual {expected: 3, actual: readVal} + + testToArray :: Effect Unit + testToArray = do + let val = [1,2,67,3,3,7,8,3,4,237] + valOut <- run do + buf <- fromArray val + toArray buf + + assertEqual {expected: val, actual: valOut} + + testFromString :: Effect Unit + testFromString = do + let str = "hello, world" + val <- run do + buf <- fromString str ASCII + read UInt8 6 buf + + assertEqual {expected: 32, actual: val} -- ASCII space + + testToFromArrayBuffer :: Effect Unit + testToFromArrayBuffer = do + buf <- run $ + fromArray [1, 2, 3] + >>= toArrayBuffer + >>= fromArrayBuffer + >>= toArray + assertEqual {expected: [1, 2, 3], actual: buf} + + testToString :: Effect Unit + testToString = do + let str = "hello, world" + strOut <-run do + buf <- fromString str ASCII + toString ASCII buf + + assertEqual {expected: str, actual: strOut} + + testReadString :: Effect Unit + testReadString = do + let str = "hello, world" + strOut <- run do + buf <- fromString str ASCII + readString ASCII 7 12 buf + + assertEqual {expected: "world", actual: strOut} + + testSlice :: Effect Unit + testSlice = do + {bufArr, bufSliceArr} <- run do + buf <- fromArray [1, 2, 3, 4] + let bufSlice = slice 1 3 buf + setAtOffset 42 1 bufSlice + bufArr <- toArray buf + bufSliceArr <- toArray bufSlice + pure {bufArr, bufSliceArr} + + assertEqual {expected: [1, 2, 42, 4], actual: bufArr} + assertEqual {expected: [2, 42], actual: bufSliceArr} + + testCopy :: Effect Unit + testCopy = do + {copied, out} <- run do + buf1 <- fromArray [1,2,3,4,5] + buf2 <- fromArray [10,9,8,7,6] + copied <- copy 0 3 buf1 2 buf2 + out <- toArray buf2 + pure {copied, out} + + assertEqual {expected: 3, actual: copied} + assertEqual {expected: [10,9,1,2,3], actual: out} + + testFill :: Effect Unit + testFill = do + out <- run do + buf <- fromArray [1,1,1,1,1] + fill 42 2 4 buf + toArray buf + + assertEqual {expected: [1,1,42,42,1], actual: out} + + testConcat' :: Effect Unit + testConcat' = do + out <- run do + bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] + buf <- concat' bufs 15 + toArray buf + + assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} + + testGetAtOffset :: Effect Unit + testGetAtOffset = do + {o1, o4, om1} <- run do + buf <- fromArray [1, 2, 3, 4] + o1 <- getAtOffset 1 buf + o4 <- getAtOffset 4 buf + om1 <- getAtOffset (-1) buf + pure {o1, o4, om1} + + assertEqual {expected: Just 2, actual: o1} + assertEqual {expected: Nothing, actual: o4} + assertEqual {expected: Nothing, actual: om1} + +testRunSt :: Effect Unit +testRunSt = do + let buf = Immutable.toArray $ runST (create 3) + assertEqual {expected: [0, 0, 0], actual: buf} diff --git a/test/Test/Node/Buffer/Immutable.purs b/test/Test/Node/Buffer/Immutable.purs new file mode 100644 index 0000000..4468d42 --- /dev/null +++ b/test/Test/Node/Buffer/Immutable.purs @@ -0,0 +1,130 @@ +module Test.Node.Buffer.Immutable (test) where + +import Prelude + +import Data.Maybe (Maybe(..)) +import Effect (Effect) +import Effect.Class.Console (log) +import Node.Buffer.Immutable as Immutable +import Node.Buffer.Immutable (ImmutableBuffer) +import Node.Buffer.Types (BufferValueType(..)) +import Node.Encoding (Encoding(..)) +import Test.Assert (assertEqual, assertTrue) + +test :: Effect Unit +test = do + log "Testing Node.Buffer.Immutable ..." + + log " - show" + testShow + + log " - eq" + testEq + + log " - compare" + testCompare + + log " - create" + testCreate + + log " - fromString" + testFromString + + log " - toString" + testToString + + log " - toArray" + testToArray + + log " - readString" + testReadString + + log " - getAtOffset" + testGetAtOffset + + log " - (to/from)ArrayBuffer" + testToFromArrayBuffer + + log " - concat'" + testConcat' + + log " - slice" + testSlice + + log " - size" + testSize + +buffer123 :: ImmutableBuffer +buffer123 = Immutable.fromArray [1, 2, 3] + +testShow :: Effect Unit +testShow = do + assertEqual {expected: "", actual: show buffer123} + +testEq :: Effect Unit +testEq = do + assertTrue $ buffer123 == buffer123 + assertTrue $ buffer123 == Immutable.fromArray [1, 2, 3] + assertTrue $ buffer123 /= Immutable.fromArray [1, 2, 4] + assertTrue $ buffer123 /= Immutable.fromArray [1, 2] + +testCompare :: Effect Unit +testCompare = do + assertEqual {expected: EQ, actual: compare buffer123 buffer123} + assertEqual {expected: LT, actual: compare buffer123 $ Immutable.fromArray [3, 2, 1]} + assertEqual {expected: GT, actual: compare buffer123 $ Immutable.fromArray [0, 1, 2]} + +testCreate :: Effect Unit +testCreate = do + assertEqual {expected: Immutable.fromArray [], actual: Immutable.create 0} + assertEqual {expected: Immutable.fromArray [0, 0, 0], actual: Immutable.create 3} + +testFromString :: Effect Unit +testFromString = do + let buf = Immutable.fromString "hello, world" ASCII + assertEqual {expected: 32, actual: Immutable.read UInt8 6 buf} + +testToString :: Effect Unit +testToString = do + let str = "hello, world" + str' = Immutable.toString ASCII $ Immutable.fromString str ASCII + assertEqual {expected: str, actual: str'} + +testToArray :: Effect Unit +testToArray = do + assertEqual {expected: [1, 2, 3], actual: Immutable.toArray buffer123} + +testReadString :: Effect Unit +testReadString = do + let str = "hello, world" + str' = Immutable.readString ASCII 7 12 $ Immutable.fromString str ASCII + assertEqual {expected: "world", actual: str'} + +testGetAtOffset :: Effect Unit +testGetAtOffset = do + assertEqual {expected: Just 2, actual: Immutable.getAtOffset 1 buffer123} + assertEqual {expected: Nothing, actual: Immutable.getAtOffset 99 buffer123} + assertEqual {expected: Nothing, actual: Immutable.getAtOffset (-1) buffer123} + +testToFromArrayBuffer :: Effect Unit +testToFromArrayBuffer = do + assertEqual {expected: buffer123, actual: Immutable.fromArrayBuffer $ Immutable.toArrayBuffer buffer123} + +testConcat' :: Effect Unit +testConcat' = do + let bufs = map (\x -> Immutable.fromArray [x, x+1, x+2]) [0,3,6,9,12] + buf = Immutable.concat' bufs 15 + out = Immutable.toArray buf + + assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} + +testSlice :: Effect Unit +testSlice = do + assertEqual {expected: buffer123, actual: Immutable.slice 0 3 buffer123} + assertEqual {expected: buffer123, actual: Immutable.slice 0 4 buffer123} + assertEqual {expected: Immutable.fromArray [2], actual: Immutable.slice 1 2 buffer123} + +testSize :: Effect Unit +testSize = do + assertEqual {expected: 0, actual: Immutable.size $ Immutable.fromArray []} + assertEqual {expected: 3, actual: Immutable.size buffer123} diff --git a/test/Test/Node/Buffer/Mutable.purs b/test/Test/Node/Buffer/Mutable.purs deleted file mode 100644 index c6d5c68..0000000 --- a/test/Test/Node/Buffer/Mutable.purs +++ /dev/null @@ -1,216 +0,0 @@ -module Test.Node.Buffer.Mutable (test) where - -import Prelude - -import Control.Monad.ST as ST -import Data.Maybe (Maybe(..)) -import Data.Traversable (traverse) -import Effect (Effect) -import Effect.Console (log) -import Node.Buffer (BufferValueType(..)) -import Node.Buffer as Buffer -import Node.Buffer.Mutable (class MutableBuffer, EffectBuffer, STBuffer, concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, toArrayBuffer, fromString, getAtOffset, setAtOffset, read, readString, runST, thaw, toArray, toString, write, slice) -import Node.Encoding (Encoding(..)) -import Test.Assert (assertEqual) -import Type.Proxy (Proxy(..)) -import Unsafe.Coerce (unsafeCoerce) - -test :: Effect Unit -test = do - - log "Testing Node.Buffer.Mutable [EffectBuffer] ..." - testMutableBuffer (Proxy :: Proxy EffectBuffer) identity - - log "Testing Node.Buffer.Mutable [STBuffer] ..." - testMutableBuffer (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) - log " - runST" - testRunSt - -testMutableBuffer :: forall buf m. MutableBuffer buf m => - Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit -testMutableBuffer _ run = do - - log " - create" - testCreate - - log " - freeze" - testFreeze - - log " - thaw" - testThaw - - log " - Reading and writing" - testReadWrite - - log " - fromArray" - testFromArray - - log " - toArray" - testToArray - - log " - fromString" - testFromString - - log " - (to/from)ArrayBuffer" - testToFromArrayBuffer - - log " - toString" - testToString - - log " - readString" - testReadString - - log " - slice" - testSlice - - log " - copy" - testCopy - - log " - fill" - testFill - - log " - concat'" - testConcat' - - log " - getAtOffset" - testGetAtOffset - - where - testCreate :: Effect Unit - testCreate = do - buf <- run (create 3 >>= toArray) - assertEqual {expected: [0, 0, 0], actual: buf} - - testFreeze :: Effect Unit - testFreeze = do - buf <- Buffer.toArray <$> run (fromArray [1, 2, 3] >>= freeze) - assertEqual {expected: [1, 2, 3], actual: buf} - - testThaw :: Effect Unit - testThaw = do - buf <- run (thaw (Buffer.fromArray [1, 2, 3]) >>= toArray) - assertEqual {expected: [1, 2, 3], actual: buf} - - testReadWrite :: Effect Unit - testReadWrite = do - let val = 42 - readVal <- run do - buf <- create 1 - write UInt8 val 0 buf - read UInt8 0 buf - - assertEqual {expected: val, actual: readVal} - - testFromArray :: Effect Unit - testFromArray = do - readVal <- run do - buf <- fromArray [1,2,3,4,5] - read UInt8 2 buf - - assertEqual {expected: 3, actual: readVal} - - testToArray :: Effect Unit - testToArray = do - let val = [1,2,67,3,3,7,8,3,4,237] - valOut <- run do - buf <- fromArray val - toArray buf - - assertEqual {expected: val, actual: valOut} - - testFromString :: Effect Unit - testFromString = do - let str = "hello, world" - val <- run do - buf <- fromString str ASCII - read UInt8 6 buf - - assertEqual {expected: 32, actual: val} -- ASCII space - - testToFromArrayBuffer :: Effect Unit - testToFromArrayBuffer = do - buf <- run $ - fromArray [1, 2, 3] - >>= toArrayBuffer - >>= fromArrayBuffer - >>= toArray - assertEqual {expected: [1, 2, 3], actual: buf} - - testToString :: Effect Unit - testToString = do - let str = "hello, world" - strOut <-run do - buf <- fromString str ASCII - toString ASCII buf - - assertEqual {expected: str, actual: strOut} - - testReadString :: Effect Unit - testReadString = do - let str = "hello, world" - strOut <- run do - buf <- fromString str ASCII - readString ASCII 7 12 buf - - assertEqual {expected: "world", actual: strOut} - - testSlice :: Effect Unit - testSlice = do - {bufArr, bufSliceArr} <- run do - buf <- fromArray [1, 2, 3, 4] - let bufSlice = slice 1 3 buf - setAtOffset 42 1 bufSlice - bufArr <- toArray buf - bufSliceArr <- toArray bufSlice - pure {bufArr, bufSliceArr} - - assertEqual {expected: [1, 2, 42, 4], actual: bufArr} - assertEqual {expected: [2, 42], actual: bufSliceArr} - - testCopy :: Effect Unit - testCopy = do - {copied, out} <- run do - buf1 <- fromArray [1,2,3,4,5] - buf2 <- fromArray [10,9,8,7,6] - copied <- copy 0 3 buf1 2 buf2 - out <- toArray buf2 - pure {copied, out} - - assertEqual {expected: 3, actual: copied} - assertEqual {expected: [10,9,1,2,3], actual: out} - - testFill :: Effect Unit - testFill = do - out <- run do - buf <- fromArray [1,1,1,1,1] - fill 42 2 4 buf - toArray buf - - assertEqual {expected: [1,1,42,42,1], actual: out} - - testConcat' :: Effect Unit - testConcat' = do - out <- run do - bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] - buf <- concat' bufs 15 - toArray buf - - assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} - - testGetAtOffset :: Effect Unit - testGetAtOffset = do - {o1, o4, om1} <- run do - buf <- fromArray [1, 2, 3, 4] - o1 <- getAtOffset 1 buf - o4 <- getAtOffset 4 buf - om1 <- getAtOffset (-1) buf - pure {o1, o4, om1} - - assertEqual {expected: Just 2, actual: o1} - assertEqual {expected: Nothing, actual: o4} - assertEqual {expected: Nothing, actual: om1} - -testRunSt :: Effect Unit -testRunSt = do - let buf = Buffer.toArray $ runST (create 3) - assertEqual {expected: [0, 0, 0], actual: buf} From 206bd4ec13197fde47849ef437ef3d0af122896a Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sun, 6 Jan 2019 16:08:48 +0000 Subject: [PATCH 08/11] Rename some internal methods for clarity (Frozen => Immutable) --- src/Node/Buffer.purs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Node/Buffer.purs b/src/Node/Buffer.purs index 7d0754f..9d11317 100644 --- a/src/Node/Buffer.purs +++ b/src/Node/Buffer.purs @@ -194,37 +194,37 @@ unsafeFreezeImpl = pure <<< unsafeCoerce unsafeThawImpl :: forall buf m. Monad m => ImmutableBuffer -> m buf unsafeThawImpl = pure <<< unsafeCoerce -usingFromFrozen :: forall buf m a. Monad m => (ImmutableBuffer -> a) -> buf -> m a -usingFromFrozen f buf = f <$> unsafeFreezeImpl buf +usingFromImmutable :: forall buf m a. Monad m => (ImmutableBuffer -> a) -> buf -> m a +usingFromImmutable f buf = f <$> unsafeFreezeImpl buf -usingToFrozen :: forall buf m a. Monad m => (a -> ImmutableBuffer) -> a -> m buf -usingToFrozen f x = unsafeThawImpl $ f x +usingToImmutable :: forall buf m a. Monad m => (a -> ImmutableBuffer) -> a -> m buf +usingToImmutable f x = unsafeThawImpl $ f x createImpl :: forall buf m. Monad m => Int -> m buf -createImpl = usingToFrozen Immutable.create +createImpl = usingToImmutable Immutable.create foreign import copyAllImpl :: forall a buf m. a -> m buf fromArrayImpl :: forall buf m. Monad m => Array Octet -> m buf -fromArrayImpl = usingToFrozen Immutable.fromArray +fromArrayImpl = usingToImmutable Immutable.fromArray fromStringImpl :: forall buf m. Monad m => String -> Encoding -> m buf -fromStringImpl s = usingToFrozen $ Immutable.fromString s +fromStringImpl s = usingToImmutable $ Immutable.fromString s fromArrayBufferImpl :: forall buf m. Monad m => ArrayBuffer -> m buf -fromArrayBufferImpl = usingToFrozen Immutable.fromArrayBuffer +fromArrayBufferImpl = usingToImmutable Immutable.fromArrayBuffer toArrayBufferImpl :: forall buf m. Monad m => buf -> m ArrayBuffer -toArrayBufferImpl = usingFromFrozen Immutable.toArrayBuffer +toArrayBufferImpl = usingFromImmutable Immutable.toArrayBuffer readImpl :: forall buf m. Monad m => BufferValueType -> Offset -> buf -> m Int -readImpl t o = usingFromFrozen $ Immutable.read t o +readImpl t o = usingFromImmutable $ Immutable.read t o readStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Offset -> buf -> m String -readStringImpl m o o' = usingFromFrozen $ Immutable.readString m o o' +readStringImpl m o o' = usingFromImmutable $ Immutable.readString m o o' toStringImpl :: forall buf m. Monad m => Encoding -> buf -> m String -toStringImpl m = usingFromFrozen $ Immutable.toString m +toStringImpl m = usingFromImmutable $ Immutable.toString m writeImpl :: forall buf m. Monad m => BufferValueType -> Int -> Offset -> buf -> m Unit writeImpl = writeInternal <<< show @@ -238,10 +238,10 @@ foreign import writeStringInternal :: forall buf m. String -> Offset -> Int -> String -> buf -> m Int toArrayImpl :: forall buf m. Monad m => buf -> m (Array Octet) -toArrayImpl = usingFromFrozen Immutable.toArray +toArrayImpl = usingFromImmutable Immutable.toArray getAtOffsetImpl :: forall buf m. Monad m => Offset -> buf -> m (Maybe Octet) -getAtOffsetImpl o = usingFromFrozen $ Immutable.getAtOffset o +getAtOffsetImpl o = usingFromImmutable $ Immutable.getAtOffset o foreign import setAtOffsetImpl :: forall buf m. Octet -> Offset -> buf -> m Unit @@ -249,7 +249,7 @@ sliceImpl :: forall buf. Offset -> Offset -> buf -> buf sliceImpl = unsafeCoerce Immutable.slice sizeImpl :: forall buf m. Monad m => buf -> m Int -sizeImpl = usingFromFrozen Immutable.size +sizeImpl = usingFromImmutable Immutable.size concatImpl :: forall buf m. Array buf -> m buf concatImpl arrs = unsafeCoerce \_ -> Immutable.concat (unsafeCoerce arrs) From 18cc642803c0d7b2d082d50bd9e76963187f1068 Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sun, 13 Jan 2019 11:44:30 +0000 Subject: [PATCH 09/11] Fix incorrect comment. --- src/Node/Buffer.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Buffer.purs b/src/Node/Buffer.purs index 9d11317..e8589d0 100644 --- a/src/Node/Buffer.purs +++ b/src/Node/Buffer.purs @@ -1,4 +1,4 @@ --- | Immutable buffers and associated operations. +-- | Mutable buffers and associated operations. module Node.Buffer ( class MutableBuffer , create From f5483d3612df4f96e24ac8fc13cb232f60cd0ae6 Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sun, 13 Jan 2019 11:54:02 +0000 Subject: [PATCH 10/11] Give MutableBuffer and it's instances a module of their own each. --- src/Node/Buffer.purs | 278 +++------------------ src/Node/Buffer/Class.purs | 115 +++++++++ src/Node/{Buffer.js => Buffer/Internal.js} | 8 +- src/Node/Buffer/Internal.purs | 109 ++++++++ src/Node/Buffer/ST.purs | 47 ++++ test/Test/Main.purs | 5 +- test/Test/Node/Buffer.purs | 211 +--------------- test/Test/Node/Buffer/Class.purs | 198 +++++++++++++++ test/Test/Node/Buffer/ST.purs | 26 ++ 9 files changed, 536 insertions(+), 461 deletions(-) create mode 100644 src/Node/Buffer/Class.purs rename src/Node/{Buffer.js => Buffer/Internal.js} (89%) create mode 100644 src/Node/Buffer/Internal.purs create mode 100644 src/Node/Buffer/ST.purs create mode 100644 test/Test/Node/Buffer/Class.purs create mode 100644 test/Test/Node/Buffer/ST.purs diff --git a/src/Node/Buffer.purs b/src/Node/Buffer.purs index e8589d0..612afd3 100644 --- a/src/Node/Buffer.purs +++ b/src/Node/Buffer.purs @@ -1,262 +1,40 @@ -- | Mutable buffers and associated operations. module Node.Buffer - ( class MutableBuffer - , create - , freeze - , unsafeFreeze - , thaw - , unsafeThaw - , fromArray - , fromString - , fromArrayBuffer - , toArrayBuffer - , read - , readString - , toString - , write - , writeString - , toArray - , getAtOffset - , setAtOffset - , slice - , size - , concat - , concat' - , copy - , fill - , Buffer - , STBuffer - , runST + ( Buffer , module TypesExports + , module Class ) where -import Prelude - -import Control.Monad.ST (ST, kind Region) -import Control.Monad.ST as ST -import Data.ArrayBuffer.Types (ArrayBuffer) -import Data.Maybe (Maybe) import Effect (Effect) -import Node.Buffer.Immutable (ImmutableBuffer) -import Node.Buffer.Immutable as Immutable -import Node.Buffer.Types (BufferValueType, Octet, Offset) +import Node.Buffer.Class (class MutableBuffer) +import Node.Buffer.Class (class MutableBuffer, concat, concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, fromString, getAtOffset, read, readString, setAtOffset, size, slice, thaw, toArray, toArrayBuffer, toString, unsafeFreeze, unsafeThaw, write, writeString) as Class +import Node.Buffer.Internal as Internal import Node.Buffer.Types (BufferValueType(..), Octet, Offset) as TypesExports -import Node.Encoding (Encoding, encodingToNode) -import Unsafe.Coerce (unsafeCoerce) - --- | A type class for mutable buffers `buf` where operations on those buffers are --- | represented by a particular monadic effect type `m`. -class Monad m <= MutableBuffer buf m | m -> buf, buf -> m where - - -- | Creates a new buffer of the specified size. - create :: Int -> m buf - - -- | Creates an immutable copy of a mutable buffer. - freeze :: buf -> m ImmutableBuffer - - -- | O(1). Convert a mutable buffer to an immutable buffer, without copying. The - -- | mutable buffer must not be mutated afterwards. - unsafeFreeze :: buf -> m ImmutableBuffer - - -- | Creates a mutable copy of an immutable buffer. - thaw :: ImmutableBuffer -> m buf - - -- | O(1) Convert an immutable buffer to a mutable buffer, without copying. The - -- | input buffer must not be used afterward. - unsafeThaw :: ImmutableBuffer -> m buf - - -- | Creates a new buffer from an array of octets, sized to match the array. - fromArray :: Array Octet -> m buf - - -- | Creates a new buffer from a string with the specified encoding, sized to - -- | match the string. - fromString :: String -> Encoding -> m buf - - -- | Creates a buffer view from a JS ArrayByffer without copying data. - fromArrayBuffer :: ArrayBuffer -> m buf - - -- | Copies the data in the buffer to a new JS ArrayBuffer - toArrayBuffer :: buf -> m ArrayBuffer - - -- | Reads a numeric value from a buffer at the specified offset. - read :: BufferValueType -> Offset -> buf -> m Int - - -- | Reads a section of a buffer as a string with the specified encoding. - readString :: Encoding -> Offset -> Offset -> buf -> m String - - -- | Reads the buffer as a string with the specified encoding. - toString :: Encoding -> buf -> m String - - -- | Writes a numeric value to a buffer at the specified offset. - write :: BufferValueType -> Int -> Offset -> buf -> m Unit - - -- | Writes octets from a string to a buffer at the specified offset. Multi-byte - -- | characters will not be written to the buffer if there is not enough capacity - -- | to write them fully. The number of bytes written is returned. - writeString :: Encoding -> Offset -> Int -> String -> buf -> m Int - - -- | Creates an array of octets from a buffer's contents. - toArray :: buf -> m (Array Octet) - - -- | Reads an octet from a buffer at the specified offset. - getAtOffset :: Offset -> buf -> m (Maybe Octet) - - -- | Writes an octet in the buffer at the specified offset. - setAtOffset :: Octet -> Offset -> buf -> m Unit - - -- | Creates a new buffer slice that acts like a window on the original buffer. - -- | Writing to the slice buffer updates the original buffer and vice-versa. - slice :: Offset -> Offset -> buf -> buf - - -- | Returns the size of a buffer. - size :: buf -> m Int - - -- | Concatenates a list of buffers. - concat :: Array buf -> m buf - - -- | Concatenates a list of buffers, combining them into a new buffer of the - -- | specified length. - concat' :: Array buf -> Int -> m buf - - -- | Copies a section of a source buffer into a target buffer at the specified - -- | offset, and returns the number of octets copied. - copy :: Offset -> Offset -> buf -> Offset -> buf -> m Int - - -- | Fills a range in a buffer with the specified octet. - fill :: Octet -> Offset -> Offset -> buf -> m Unit -- | A reference to a mutable buffer for use with `Effect` foreign import data Buffer :: Type --- | A reference to a mutable buffer for use with `ST` --- | --- | The type parameter represents the memory region which the buffer belongs to. -foreign import data STBuffer :: Region -> Type - --- | Runs an effect creating an `STBuffer` then freezes the buffer and returns --- | it, without unneccessary copying. -runST :: (forall h. ST h (STBuffer h)) -> ImmutableBuffer -runST st = ST.run (st >>= unsafeFreeze) - instance mutableBufferEffect :: MutableBuffer Buffer Effect where - create = createImpl - freeze = copyAllImpl - unsafeFreeze = unsafeFreezeImpl - thaw = copyAllImpl - unsafeThaw = unsafeThawImpl - fromArray = fromArrayImpl - fromString = fromStringImpl - fromArrayBuffer = fromArrayBufferImpl - toArrayBuffer = toArrayBufferImpl - read = readImpl - readString = readStringImpl - toString = toStringImpl - write = writeImpl - writeString = writeStringImpl - toArray = toArrayImpl - getAtOffset = getAtOffsetImpl - setAtOffset = setAtOffsetImpl - slice = sliceImpl - size = sizeImpl - concat = concatImpl - concat' = concatImpl' - copy = copyImpl - fill = fillImpl - -instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where - create = createImpl - freeze = copyAllImpl - unsafeFreeze = unsafeFreezeImpl - thaw = copyAllImpl - unsafeThaw = unsafeThawImpl - fromArray = fromArrayImpl - fromString = fromStringImpl - fromArrayBuffer = fromArrayBufferImpl - toArrayBuffer = toArrayBufferImpl - read = readImpl - readString = readStringImpl - toString = toStringImpl - write = writeImpl - writeString = writeStringImpl - toArray = toArrayImpl - getAtOffset = getAtOffsetImpl - setAtOffset = setAtOffsetImpl - slice = sliceImpl - size = sizeImpl - concat = concatImpl - concat' = concatImpl' - copy = copyImpl - fill = fillImpl - -unsafeFreezeImpl :: forall buf m. Monad m => buf -> m ImmutableBuffer -unsafeFreezeImpl = pure <<< unsafeCoerce - -unsafeThawImpl :: forall buf m. Monad m => ImmutableBuffer -> m buf -unsafeThawImpl = pure <<< unsafeCoerce - -usingFromImmutable :: forall buf m a. Monad m => (ImmutableBuffer -> a) -> buf -> m a -usingFromImmutable f buf = f <$> unsafeFreezeImpl buf - -usingToImmutable :: forall buf m a. Monad m => (a -> ImmutableBuffer) -> a -> m buf -usingToImmutable f x = unsafeThawImpl $ f x - -createImpl :: forall buf m. Monad m => Int -> m buf -createImpl = usingToImmutable Immutable.create - -foreign import copyAllImpl :: forall a buf m. a -> m buf - -fromArrayImpl :: forall buf m. Monad m => Array Octet -> m buf -fromArrayImpl = usingToImmutable Immutable.fromArray - -fromStringImpl :: forall buf m. Monad m => String -> Encoding -> m buf -fromStringImpl s = usingToImmutable $ Immutable.fromString s - -fromArrayBufferImpl :: forall buf m. Monad m => ArrayBuffer -> m buf -fromArrayBufferImpl = usingToImmutable Immutable.fromArrayBuffer - -toArrayBufferImpl :: forall buf m. Monad m => buf -> m ArrayBuffer -toArrayBufferImpl = usingFromImmutable Immutable.toArrayBuffer - -readImpl :: forall buf m. Monad m => BufferValueType -> Offset -> buf -> m Int -readImpl t o = usingFromImmutable $ Immutable.read t o - -readStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Offset -> buf -> m String -readStringImpl m o o' = usingFromImmutable $ Immutable.readString m o o' - -toStringImpl :: forall buf m. Monad m => Encoding -> buf -> m String -toStringImpl m = usingFromImmutable $ Immutable.toString m - -writeImpl :: forall buf m. Monad m => BufferValueType -> Int -> Offset -> buf -> m Unit -writeImpl = writeInternal <<< show - -foreign import writeInternal :: forall buf m. String -> Int -> Offset -> buf -> m Unit - -writeStringImpl :: forall buf m. Monad m => Encoding -> Offset -> Int -> String -> buf -> m Int -writeStringImpl = writeStringInternal <<< encodingToNode - -foreign import writeStringInternal :: - forall buf m. String -> Offset -> Int -> String -> buf -> m Int - -toArrayImpl :: forall buf m. Monad m => buf -> m (Array Octet) -toArrayImpl = usingFromImmutable Immutable.toArray - -getAtOffsetImpl :: forall buf m. Monad m => Offset -> buf -> m (Maybe Octet) -getAtOffsetImpl o = usingFromImmutable $ Immutable.getAtOffset o - -foreign import setAtOffsetImpl :: forall buf m. Octet -> Offset -> buf -> m Unit - -sliceImpl :: forall buf. Offset -> Offset -> buf -> buf -sliceImpl = unsafeCoerce Immutable.slice - -sizeImpl :: forall buf m. Monad m => buf -> m Int -sizeImpl = usingFromImmutable Immutable.size - -concatImpl :: forall buf m. Array buf -> m buf -concatImpl arrs = unsafeCoerce \_ -> Immutable.concat (unsafeCoerce arrs) - -concatImpl' :: forall buf m. Monad m => Array buf -> Int -> m buf -concatImpl' arrs n = unsafeCoerce \_ -> Immutable.concat' (unsafeCoerce arrs) n - -foreign import copyImpl :: forall buf m. Offset -> Offset -> buf -> Offset -> buf -> m Int - -foreign import fillImpl :: forall buf m. Octet -> Offset -> Offset -> buf -> m Unit + create = Internal.create + freeze = Internal.copyAll + unsafeFreeze = Internal.unsafeFreeze + thaw = Internal.copyAll + unsafeThaw = Internal.unsafeThaw + fromArray = Internal.fromArray + fromString = Internal.fromString + fromArrayBuffer = Internal.fromArrayBuffer + toArrayBuffer = Internal.toArrayBuffer + read = Internal.read + readString = Internal.readString + toString = Internal.toString + write = Internal.write + writeString = Internal.writeString + toArray = Internal.toArray + getAtOffset = Internal.getAtOffset + setAtOffset = Internal.setAtOffset + slice = Internal.slice + size = Internal.size + concat = Internal.concat + concat' = Internal.concat' + copy = Internal.copy + fill = Internal.fill diff --git a/src/Node/Buffer/Class.purs b/src/Node/Buffer/Class.purs new file mode 100644 index 0000000..1bf2702 --- /dev/null +++ b/src/Node/Buffer/Class.purs @@ -0,0 +1,115 @@ +module Node.Buffer.Class + ( class MutableBuffer + , create + , freeze + , unsafeFreeze + , thaw + , unsafeThaw + , fromArray + , fromString + , fromArrayBuffer + , toArrayBuffer + , read + , readString + , toString + , write + , writeString + , toArray + , getAtOffset + , setAtOffset + , slice + , size + , concat + , concat' + , copy + , fill + ) where + +import Prelude + +import Data.ArrayBuffer.Types (ArrayBuffer) +import Data.Maybe (Maybe) +import Node.Buffer.Immutable (ImmutableBuffer) +import Node.Buffer.Types (BufferValueType, Octet, Offset) +import Node.Encoding (Encoding) + +-- | A type class for mutable buffers `buf` where operations on those buffers are +-- | represented by a particular monadic effect type `m`. +class Monad m <= MutableBuffer buf m | buf -> m where + + -- | Creates a new buffer of the specified size. + create :: Int -> m buf + + -- | Creates an immutable copy of a mutable buffer. + freeze :: buf -> m ImmutableBuffer + + -- | O(1). Convert a mutable buffer to an immutable buffer, without copying. The + -- | mutable buffer must not be mutated afterwards. + unsafeFreeze :: buf -> m ImmutableBuffer + + -- | Creates a mutable copy of an immutable buffer. + thaw :: ImmutableBuffer -> m buf + + -- | O(1) Convert an immutable buffer to a mutable buffer, without copying. The + -- | input buffer must not be used afterward. + unsafeThaw :: ImmutableBuffer -> m buf + + -- | Creates a new buffer from an array of octets, sized to match the array. + fromArray :: Array Octet -> m buf + + -- | Creates a new buffer from a string with the specified encoding, sized to + -- | match the string. + fromString :: String -> Encoding -> m buf + + -- | Creates a buffer view from a JS ArrayByffer without copying data. + fromArrayBuffer :: ArrayBuffer -> m buf + + -- | Copies the data in the buffer to a new JS ArrayBuffer + toArrayBuffer :: buf -> m ArrayBuffer + + -- | Reads a numeric value from a buffer at the specified offset. + read :: BufferValueType -> Offset -> buf -> m Int + + -- | Reads a section of a buffer as a string with the specified encoding. + readString :: Encoding -> Offset -> Offset -> buf -> m String + + -- | Reads the buffer as a string with the specified encoding. + toString :: Encoding -> buf -> m String + + -- | Writes a numeric value to a buffer at the specified offset. + write :: BufferValueType -> Int -> Offset -> buf -> m Unit + + -- | Writes octets from a string to a buffer at the specified offset. Multi-byte + -- | characters will not be written to the buffer if there is not enough capacity + -- | to write them fully. The number of bytes written is returned. + writeString :: Encoding -> Offset -> Int -> String -> buf -> m Int + + -- | Creates an array of octets from a buffer's contents. + toArray :: buf -> m (Array Octet) + + -- | Reads an octet from a buffer at the specified offset. + getAtOffset :: Offset -> buf -> m (Maybe Octet) + + -- | Writes an octet in the buffer at the specified offset. + setAtOffset :: Octet -> Offset -> buf -> m Unit + + -- | Creates a new buffer slice that acts like a window on the original buffer. + -- | Writing to the slice buffer updates the original buffer and vice-versa. + slice :: Offset -> Offset -> buf -> buf + + -- | Returns the size of a buffer. + size :: buf -> m Int + + -- | Concatenates a list of buffers. + concat :: Array buf -> m buf + + -- | Concatenates a list of buffers, combining them into a new buffer of the + -- | specified length. + concat' :: Array buf -> Int -> m buf + + -- | Copies a section of a source buffer into a target buffer at the specified + -- | offset, and returns the number of octets copied. + copy :: Offset -> Offset -> buf -> Offset -> buf -> m Int + + -- | Fills a range in a buffer with the specified octet. + fill :: Octet -> Offset -> Offset -> buf -> m Unit diff --git a/src/Node/Buffer.js b/src/Node/Buffer/Internal.js similarity index 89% rename from src/Node/Buffer.js rename to src/Node/Buffer/Internal.js index 95b7a5d..7cd3cd9 100644 --- a/src/Node/Buffer.js +++ b/src/Node/Buffer/Internal.js @@ -2,7 +2,7 @@ /* global Buffer */ "use strict"; -exports.copyAllImpl = function(a) { +exports.copyAll = function(a) { return function() { return Buffer.from(a); }; @@ -35,7 +35,7 @@ exports.writeStringInternal = function (encoding) { }; }; -exports.setAtOffsetImpl = function (value) { +exports.setAtOffset = function (value) { return function (offset) { return function (buff) { return function() { @@ -46,7 +46,7 @@ exports.setAtOffsetImpl = function (value) { }; }; -exports.copyImpl = function (srcStart) { +exports.copy = function (srcStart) { return function (srcEnd) { return function (src) { return function (targStart) { @@ -60,7 +60,7 @@ exports.copyImpl = function (srcStart) { }; }; -exports.fillImpl = function (octet) { +exports.fill = function (octet) { return function (start) { return function (end) { return function (buf) { diff --git a/src/Node/Buffer/Internal.purs b/src/Node/Buffer/Internal.purs new file mode 100644 index 0000000..6ee6662 --- /dev/null +++ b/src/Node/Buffer/Internal.purs @@ -0,0 +1,109 @@ +-- | Functions and types to support the other modules. Not for public use. +module Node.Buffer.Internal + ( unsafeFreeze + , unsafeThaw + , usingFromImmutable + , usingToImmutable + , create + , copyAll + , fromArray + , fromString + , fromArrayBuffer + , toArrayBuffer + , read + , readString + , toString + , write + , writeString + , toArray + , getAtOffset + , setAtOffset + , slice + , size + , concat + , concat' + , copy + , fill ) where + +import Prelude + +import Data.ArrayBuffer.Types (ArrayBuffer) +import Data.Maybe (Maybe) +import Node.Buffer.Immutable (ImmutableBuffer) +import Node.Buffer.Immutable as Immutable +import Node.Buffer.Types (BufferValueType, Octet, Offset) +import Node.Encoding (Encoding, encodingToNode) +import Unsafe.Coerce (unsafeCoerce) + +unsafeFreeze :: forall buf m. Monad m => buf -> m ImmutableBuffer +unsafeFreeze = pure <<< unsafeCoerce + +unsafeThaw :: forall buf m. Monad m => ImmutableBuffer -> m buf +unsafeThaw = pure <<< unsafeCoerce + +usingFromImmutable :: forall buf m a. Monad m => (ImmutableBuffer -> a) -> buf -> m a +usingFromImmutable f buf = f <$> unsafeFreeze buf + +usingToImmutable :: forall buf m a. Monad m => (a -> ImmutableBuffer) -> a -> m buf +usingToImmutable f x = unsafeThaw $ f x + +create :: forall buf m. Monad m => Int -> m buf +create = usingToImmutable Immutable.create + +foreign import copyAll :: forall a buf m. a -> m buf + +fromArray :: forall buf m. Monad m => Array Octet -> m buf +fromArray = usingToImmutable Immutable.fromArray + +fromString :: forall buf m. Monad m => String -> Encoding -> m buf +fromString s = usingToImmutable $ Immutable.fromString s + +fromArrayBuffer :: forall buf m. Monad m => ArrayBuffer -> m buf +fromArrayBuffer = usingToImmutable Immutable.fromArrayBuffer + +toArrayBuffer :: forall buf m. Monad m => buf -> m ArrayBuffer +toArrayBuffer = usingFromImmutable Immutable.toArrayBuffer + +read :: forall buf m. Monad m => BufferValueType -> Offset -> buf -> m Int +read t o = usingFromImmutable $ Immutable.read t o + +readString :: forall buf m. Monad m => Encoding -> Offset -> Offset -> buf -> m String +readString m o o' = usingFromImmutable $ Immutable.readString m o o' + +toString :: forall buf m. Monad m => Encoding -> buf -> m String +toString m = usingFromImmutable $ Immutable.toString m + +write :: forall buf m. Monad m => BufferValueType -> Int -> Offset -> buf -> m Unit +write = writeInternal <<< show + +foreign import writeInternal :: forall buf m. String -> Int -> Offset -> buf -> m Unit + +writeString :: forall buf m. Monad m => Encoding -> Offset -> Int -> String -> buf -> m Int +writeString = writeStringInternal <<< encodingToNode + +foreign import writeStringInternal :: + forall buf m. String -> Offset -> Int -> String -> buf -> m Int + +toArray :: forall buf m. Monad m => buf -> m (Array Octet) +toArray = usingFromImmutable Immutable.toArray + +getAtOffset :: forall buf m. Monad m => Offset -> buf -> m (Maybe Octet) +getAtOffset o = usingFromImmutable $ Immutable.getAtOffset o + +foreign import setAtOffset :: forall buf m. Octet -> Offset -> buf -> m Unit + +slice :: forall buf. Offset -> Offset -> buf -> buf +slice = unsafeCoerce Immutable.slice + +size :: forall buf m. Monad m => buf -> m Int +size = usingFromImmutable Immutable.size + +concat :: forall buf m. Array buf -> m buf +concat arrs = unsafeCoerce \_ -> Immutable.concat (unsafeCoerce arrs) + +concat' :: forall buf m. Monad m => Array buf -> Int -> m buf +concat' arrs n = unsafeCoerce \_ -> Immutable.concat' (unsafeCoerce arrs) n + +foreign import copy :: forall buf m. Offset -> Offset -> buf -> Offset -> buf -> m Int + +foreign import fill :: forall buf m. Octet -> Offset -> Offset -> buf -> m Unit diff --git a/src/Node/Buffer/ST.purs b/src/Node/Buffer/ST.purs new file mode 100644 index 0000000..ab13182 --- /dev/null +++ b/src/Node/Buffer/ST.purs @@ -0,0 +1,47 @@ +module Node.Buffer.ST + ( STBuffer + , run + ) where + +import Prelude + +import Control.Monad.ST (ST, kind Region) +import Control.Monad.ST as ST +import Node.Buffer.Class (class MutableBuffer, unsafeFreeze) +import Node.Buffer.Immutable (ImmutableBuffer) +import Node.Buffer.Internal as Internal + +-- | A reference to a mutable buffer for use with `ST` +-- | +-- | The type parameter represents the memory region which the buffer belongs to. +foreign import data STBuffer :: Region -> Type + +-- | Runs an effect creating an `STBuffer` then freezes the buffer and returns +-- | it, without unneccessary copying. +run :: (forall h. ST h (STBuffer h)) -> ImmutableBuffer +run st = ST.run (st >>= unsafeFreeze) + +instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where + create = Internal.create + freeze = Internal.copyAll + unsafeFreeze = Internal.unsafeFreeze + thaw = Internal.copyAll + unsafeThaw = Internal.unsafeThaw + fromArray = Internal.fromArray + fromString = Internal.fromString + fromArrayBuffer = Internal.fromArrayBuffer + toArrayBuffer = Internal.toArrayBuffer + read = Internal.read + readString = Internal.readString + toString = Internal.toString + write = Internal.write + writeString = Internal.writeString + toArray = Internal.toArray + getAtOffset = Internal.getAtOffset + setAtOffset = Internal.setAtOffset + slice = Internal.slice + size = Internal.size + concat = Internal.concat + concat' = Internal.concat' + copy = Internal.copy + fill = Internal.fill diff --git a/test/Test/Main.purs b/test/Test/Main.purs index ba40432..7cdb68d 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -3,9 +3,6 @@ module Test.Main where import Prelude import Effect (Effect) import Test.Node.Buffer as Buffer -import Test.Node.Buffer.Immutable as Immutable main :: Effect Unit -main = do - Buffer.test - Immutable.test +main = Buffer.test diff --git a/test/Test/Node/Buffer.purs b/test/Test/Node/Buffer.purs index 21b848b..85a537a 100644 --- a/test/Test/Node/Buffer.purs +++ b/test/Test/Node/Buffer.purs @@ -2,214 +2,19 @@ module Test.Node.Buffer (test) where import Prelude -import Control.Monad.ST as ST -import Data.Maybe (Maybe(..)) -import Data.Traversable (traverse) import Effect (Effect) import Effect.Console (log) -import Node.Buffer (class MutableBuffer, Buffer, STBuffer, BufferValueType(..), concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, toArrayBuffer, fromString, getAtOffset, setAtOffset, read, readString, runST, thaw, toArray, toString, write, slice) -import Node.Buffer.Immutable as Immutable -import Node.Encoding (Encoding(..)) -import Test.Assert (assertEqual) +import Node.Buffer (Buffer) +import Test.Node.Buffer.Class (testMutableBuffer) +import Test.Node.Buffer.Immutable as Immutable +import Test.Node.Buffer.ST (test) as ST import Type.Proxy (Proxy(..)) -import Unsafe.Coerce (unsafeCoerce) test :: Effect Unit test = do - log "Testing Node.Buffer [Effect] ..." - testBuffer (Proxy :: Proxy Buffer) identity + log "Testing Node.Buffer ..." + testMutableBuffer (Proxy :: Proxy Buffer) identity - log "Testing Node.Buffer [ST] ..." - testBuffer (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) - log " - runST" - testRunSt - -testBuffer :: forall buf m. MutableBuffer buf m => - Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit -testBuffer _ run = do - - log " - create" - testCreate - - log " - freeze" - testFreeze - - log " - thaw" - testThaw - - log " - Reading and writing" - testReadWrite - - log " - fromArray" - testFromArray - - log " - toArray" - testToArray - - log " - fromString" - testFromString - - log " - (to/from)ArrayBuffer" - testToFromArrayBuffer - - log " - toString" - testToString - - log " - readString" - testReadString - - log " - slice" - testSlice - - log " - copy" - testCopy - - log " - fill" - testFill - - log " - concat'" - testConcat' - - log " - getAtOffset" - testGetAtOffset - - where - testCreate :: Effect Unit - testCreate = do - buf <- run (create 3 >>= toArray) - assertEqual {expected: [0, 0, 0], actual: buf} - - testFreeze :: Effect Unit - testFreeze = do - buf <- Immutable.toArray <$> run (fromArray [1, 2, 3] >>= freeze) - assertEqual {expected: [1, 2, 3], actual: buf} - - testThaw :: Effect Unit - testThaw = do - buf <- run (thaw (Immutable.fromArray [1, 2, 3]) >>= toArray) - assertEqual {expected: [1, 2, 3], actual: buf} - - testReadWrite :: Effect Unit - testReadWrite = do - let val = 42 - readVal <- run do - buf <- create 1 - write UInt8 val 0 buf - read UInt8 0 buf - - assertEqual {expected: val, actual: readVal} - - testFromArray :: Effect Unit - testFromArray = do - readVal <- run do - buf <- fromArray [1,2,3,4,5] - read UInt8 2 buf - - assertEqual {expected: 3, actual: readVal} - - testToArray :: Effect Unit - testToArray = do - let val = [1,2,67,3,3,7,8,3,4,237] - valOut <- run do - buf <- fromArray val - toArray buf - - assertEqual {expected: val, actual: valOut} - - testFromString :: Effect Unit - testFromString = do - let str = "hello, world" - val <- run do - buf <- fromString str ASCII - read UInt8 6 buf - - assertEqual {expected: 32, actual: val} -- ASCII space - - testToFromArrayBuffer :: Effect Unit - testToFromArrayBuffer = do - buf <- run $ - fromArray [1, 2, 3] - >>= toArrayBuffer - >>= fromArrayBuffer - >>= toArray - assertEqual {expected: [1, 2, 3], actual: buf} - - testToString :: Effect Unit - testToString = do - let str = "hello, world" - strOut <-run do - buf <- fromString str ASCII - toString ASCII buf - - assertEqual {expected: str, actual: strOut} - - testReadString :: Effect Unit - testReadString = do - let str = "hello, world" - strOut <- run do - buf <- fromString str ASCII - readString ASCII 7 12 buf - - assertEqual {expected: "world", actual: strOut} - - testSlice :: Effect Unit - testSlice = do - {bufArr, bufSliceArr} <- run do - buf <- fromArray [1, 2, 3, 4] - let bufSlice = slice 1 3 buf - setAtOffset 42 1 bufSlice - bufArr <- toArray buf - bufSliceArr <- toArray bufSlice - pure {bufArr, bufSliceArr} - - assertEqual {expected: [1, 2, 42, 4], actual: bufArr} - assertEqual {expected: [2, 42], actual: bufSliceArr} - - testCopy :: Effect Unit - testCopy = do - {copied, out} <- run do - buf1 <- fromArray [1,2,3,4,5] - buf2 <- fromArray [10,9,8,7,6] - copied <- copy 0 3 buf1 2 buf2 - out <- toArray buf2 - pure {copied, out} - - assertEqual {expected: 3, actual: copied} - assertEqual {expected: [10,9,1,2,3], actual: out} - - testFill :: Effect Unit - testFill = do - out <- run do - buf <- fromArray [1,1,1,1,1] - fill 42 2 4 buf - toArray buf - - assertEqual {expected: [1,1,42,42,1], actual: out} - - testConcat' :: Effect Unit - testConcat' = do - out <- run do - bufs <- traverse fromArray $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] - buf <- concat' bufs 15 - toArray buf - - assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} - - testGetAtOffset :: Effect Unit - testGetAtOffset = do - {o1, o4, om1} <- run do - buf <- fromArray [1, 2, 3, 4] - o1 <- getAtOffset 1 buf - o4 <- getAtOffset 4 buf - om1 <- getAtOffset (-1) buf - pure {o1, o4, om1} - - assertEqual {expected: Just 2, actual: o1} - assertEqual {expected: Nothing, actual: o4} - assertEqual {expected: Nothing, actual: om1} - -testRunSt :: Effect Unit -testRunSt = do - let buf = Immutable.toArray $ runST (create 3) - assertEqual {expected: [0, 0, 0], actual: buf} + ST.test + Immutable.test diff --git a/test/Test/Node/Buffer/Class.purs b/test/Test/Node/Buffer/Class.purs new file mode 100644 index 0000000..633179d --- /dev/null +++ b/test/Test/Node/Buffer/Class.purs @@ -0,0 +1,198 @@ +module Test.Node.Buffer.Class (testMutableBuffer) where + +import Prelude + +import Data.ArrayBuffer.Types (ArrayBuffer) +import Data.Maybe (Maybe(..)) +import Data.Traversable (traverse) +import Effect (Effect) +import Effect.Console (log) +import Node.Buffer (class MutableBuffer, BufferValueType(..), concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, fromString, getAtOffset, read, readString, setAtOffset, slice, thaw, toArray, toArrayBuffer, toString, write) +import Node.Buffer.Immutable as Immutable +import Node.Encoding (Encoding(..)) +import Test.Assert (assertEqual) +import Type.Proxy (Proxy) + +testMutableBuffer :: forall buf m. MutableBuffer buf m => + Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit +testMutableBuffer _ run = do + + log " - create" + testCreate + + log " - freeze" + testFreeze + + log " - thaw" + testThaw + + log " - Reading and writing" + testReadWrite + + log " - fromArray" + testFromArray + + log " - toArray" + testToArray + + log " - fromString" + testFromString + + log " - (to/from)ArrayBuffer" + testToFromArrayBuffer + + log " - toString" + testToString + + log " - readString" + testReadString + + log " - slice" + testSlice + + log " - copy" + testCopy + + log " - fill" + testFill + + log " - concat'" + testConcat' + + log " - getAtOffset" + testGetAtOffset + + where + testCreate :: Effect Unit + testCreate = do + buf <- run ((create 3 :: m buf) >>= toArray) + assertEqual {expected: [0, 0, 0], actual: buf} + + testFreeze :: Effect Unit + testFreeze = do + buf <- Immutable.toArray <$> run ((fromArray [1, 2, 3] :: m buf) >>= freeze) + assertEqual {expected: [1, 2, 3], actual: buf} + + testThaw :: Effect Unit + testThaw = do + buf <- run ((thaw (Immutable.fromArray [1, 2, 3]) :: m buf) >>= toArray) + assertEqual {expected: [1, 2, 3], actual: buf} + + testReadWrite :: Effect Unit + testReadWrite = do + let val = 42 + readVal <- run do + buf <- create 1 :: m buf + write UInt8 val 0 buf + read UInt8 0 buf + + assertEqual {expected: val, actual: readVal} + + testFromArray :: Effect Unit + testFromArray = do + readVal <- run do + buf <- fromArray [1,2,3,4,5] :: m buf + read UInt8 2 buf + + assertEqual {expected: 3, actual: readVal} + + testToArray :: Effect Unit + testToArray = do + let val = [1,2,67,3,3,7,8,3,4,237] + valOut <- run do + buf <- fromArray val :: m buf + toArray buf + + assertEqual {expected: val, actual: valOut} + + testFromString :: Effect Unit + testFromString = do + let str = "hello, world" + val <- run do + buf <- fromString str ASCII :: m buf + read UInt8 6 buf + + assertEqual {expected: 32, actual: val} -- ASCII space + + testToFromArrayBuffer :: Effect Unit + testToFromArrayBuffer = do + buf <- run $ + fromArray [1, 2, 3] + >>= (toArrayBuffer :: buf -> m ArrayBuffer) + >>= (fromArrayBuffer :: ArrayBuffer -> m buf) + >>= toArray + assertEqual {expected: [1, 2, 3], actual: buf} + + testToString :: Effect Unit + testToString = do + let str = "hello, world" + strOut <- run do + buf <- fromString str ASCII :: m buf + toString ASCII buf + + assertEqual {expected: str, actual: strOut} + + testReadString :: Effect Unit + testReadString = do + let str = "hello, world" + strOut <- run do + buf <- fromString str ASCII :: m buf + readString ASCII 7 12 buf + + assertEqual {expected: "world", actual: strOut} + + testSlice :: Effect Unit + testSlice = do + {bufArr, bufSliceArr} <- run do + buf <- fromArray [1, 2, 3, 4] :: m buf + let bufSlice = slice 1 3 buf + setAtOffset 42 1 bufSlice + bufArr <- toArray buf + bufSliceArr <- toArray bufSlice + pure {bufArr, bufSliceArr} + + assertEqual {expected: [1, 2, 42, 4], actual: bufArr} + assertEqual {expected: [2, 42], actual: bufSliceArr} + + testCopy :: Effect Unit + testCopy = do + {copied, out} <- run do + buf1 <- fromArray [1,2,3,4,5] :: m buf + buf2 <- fromArray [10,9,8,7,6] + copied <- copy 0 3 buf1 2 buf2 + out <- toArray buf2 + pure {copied, out} + + assertEqual {expected: 3, actual: copied} + assertEqual {expected: [10,9,1,2,3], actual: out} + + testFill :: Effect Unit + testFill = do + out <- run do + buf <- fromArray [1,1,1,1,1] :: m buf + fill 42 2 4 buf + toArray buf + + assertEqual {expected: [1,1,42,42,1], actual: out} + + testConcat' :: Effect Unit + testConcat' = do + out <- run do + bufs <- traverse (fromArray :: Array Int -> m buf) $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12] + buf <- concat' bufs 15 + toArray buf + + assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out} + + testGetAtOffset :: Effect Unit + testGetAtOffset = do + {o1, o4, om1} <- run do + buf <- fromArray [1, 2, 3, 4] :: m buf + o1 <- getAtOffset 1 buf + o4 <- getAtOffset 4 buf + om1 <- getAtOffset (-1) buf + pure {o1, o4, om1} + + assertEqual {expected: Just 2, actual: o1} + assertEqual {expected: Nothing, actual: o4} + assertEqual {expected: Nothing, actual: om1} diff --git a/test/Test/Node/Buffer/ST.purs b/test/Test/Node/Buffer/ST.purs new file mode 100644 index 0000000..222f3c1 --- /dev/null +++ b/test/Test/Node/Buffer/ST.purs @@ -0,0 +1,26 @@ +module Test.Node.Buffer.ST (test) where + +import Prelude + +import Control.Monad.ST (run) as ST +import Effect (Effect) +import Effect.Console (log) +import Node.Buffer.Class (create) +import Node.Buffer.Immutable as Immutable +import Node.Buffer.ST (STBuffer, run) +import Test.Assert (assertEqual) +import Test.Node.Buffer.Class (testMutableBuffer) +import Type.Proxy (Proxy(..)) +import Unsafe.Coerce (unsafeCoerce) + +test :: Effect Unit +test = do + log "Testing Node.Buffer.ST ..." + testMutableBuffer (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) + log " - run" + testRun + +testRun :: Effect Unit +testRun = do + let buf = Immutable.toArray $ run (create 3) + assertEqual {expected: [0, 0, 0], actual: buf} From 7a48e3333792fedf85293fbdbfe41a17fb1d1566 Mon Sep 17 00:00:00 2001 From: Gareth Daniel Smith Date: Sun, 13 Jan 2019 21:30:01 +0000 Subject: [PATCH 11/11] Simplify `forall h a. ST h a -> Effect a` function. --- test/Test/Node/Buffer/ST.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Test/Node/Buffer/ST.purs b/test/Test/Node/Buffer/ST.purs index 222f3c1..783f835 100644 --- a/test/Test/Node/Buffer/ST.purs +++ b/test/Test/Node/Buffer/ST.purs @@ -16,7 +16,7 @@ import Unsafe.Coerce (unsafeCoerce) test :: Effect Unit test = do log "Testing Node.Buffer.ST ..." - testMutableBuffer (Proxy :: Proxy (STBuffer _)) (unsafeCoerce ST.run >>> pure) + testMutableBuffer (Proxy :: Proxy (STBuffer _)) unsafeCoerce log " - run" testRun