Skip to content

Commit 7098df6

Browse files
authoredMar 10, 2019
Merge pull request #33 from Dretch/stbuffer-typeclass
Explore using ST for effects (issue #24)
2 parents d279daa + 7a48e33 commit 7098df6

18 files changed

+1024
-493
lines changed
 

‎bower.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
"dependencies": {
1616
"purescript-effect": "^2.0.0",
1717
"purescript-maybe": "^4.0.0",
18-
"purescript-arraybuffer-types": "^2.0.0"
18+
"purescript-arraybuffer-types": "^2.0.0",
19+
"purescript-st": "^4.0.0",
20+
"purescript-unsafe-coerce": "^4.0.0"
1921
},
2022
"devDependencies": {
2123
"purescript-assert": "^4.0.0",
2224
"purescript-console": "^4.1.0",
23-
"purescript-foldable-traversable": "^4.0.0"
25+
"purescript-foldable-traversable": "^4.0.0",
26+
"purescript-proxy": "^3.0.0"
2427
}
2528
}

‎src/Node/Buffer.js

-176
This file was deleted.

‎src/Node/Buffer.purs

+33-157
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,40 @@
1+
-- | Mutable buffers and associated operations.
12
module Node.Buffer
2-
( Octet()
3-
, Offset()
4-
, Buffer()
5-
, BufferValueType(..)
6-
, create
7-
, fromArray
8-
, fromString
9-
, fromArrayBuffer
10-
, toArrayBuffer
11-
, read
12-
, readString
13-
, toString
14-
, write
15-
, writeString
16-
, toArray
17-
, getAtOffset
18-
, setAtOffset
19-
, size
20-
, concat
21-
, concat'
22-
, copy
23-
, fill
3+
( Buffer
4+
, module TypesExports
5+
, module Class
246
) where
257

26-
import Prelude
27-
288
import Effect (Effect)
29-
import Data.ArrayBuffer.Types (ArrayBuffer)
30-
import Data.Maybe (Maybe(..))
31-
import Node.Encoding (Encoding, encodingToNode)
32-
33-
-- | Type synonym indicating the value should be an octet (0-255). If the value
34-
-- | provided is outside this range it will be used as modulo 256.
35-
type Octet = Int
9+
import Node.Buffer.Class (class MutableBuffer)
10+
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
11+
import Node.Buffer.Internal as Internal
12+
import Node.Buffer.Types (BufferValueType(..), Octet, Offset) as TypesExports
3613

37-
-- | Type synonym indicating the value refers to an offset in a buffer.
38-
type Offset = Int
39-
40-
-- | An instance of Node's Buffer class.
14+
-- | A reference to a mutable buffer for use with `Effect`
4115
foreign import data Buffer :: Type
4216

43-
instance showBuffer :: Show Buffer where
44-
show = showImpl
45-
46-
foreign import showImpl :: Buffer -> String
47-
48-
-- | Enumeration of the numeric types that can be written to a buffer.
49-
data BufferValueType
50-
= UInt8
51-
| UInt16LE
52-
| UInt16BE
53-
| UInt32LE
54-
| UInt32BE
55-
| Int8
56-
| Int16LE
57-
| Int16BE
58-
| Int32LE
59-
| Int32BE
60-
| FloatLE
61-
| FloatBE
62-
| DoubleLE
63-
| DoubleBE
64-
65-
instance showBufferValueType :: Show BufferValueType where
66-
show UInt8 = "UInt8"
67-
show UInt16LE = "UInt16LE"
68-
show UInt16BE = "UInt16BE"
69-
show UInt32LE = "UInt32LE"
70-
show UInt32BE = "UInt32BE"
71-
show Int8 = "Int8"
72-
show Int16LE = "Int16LE"
73-
show Int16BE = "Int16BE"
74-
show Int32LE = "Int32LE"
75-
show Int32BE = "Int32BE"
76-
show FloatLE = "FloatLE"
77-
show FloatBE = "FloatBE"
78-
show DoubleLE = "DoubleLE"
79-
show DoubleBE = "DoubleBE"
80-
81-
-- | Creates a new buffer of the specified size.
82-
foreign import create :: Int -> Effect Buffer
83-
84-
-- | Creates a new buffer from an array of octets, sized to match the array.
85-
foreign import fromArray :: Array Octet -> Effect Buffer
86-
87-
-- | Creates a buffer view from a JS ArrayByffer without copying data.
88-
--
89-
-- Requires Node >= v5.10.0
90-
foreign import fromArrayBuffer :: ArrayBuffer -> Effect Buffer
91-
92-
-- | Creates a new buffer from a string with the specified encoding, sized to
93-
-- | match the string.
94-
fromString :: String -> Encoding -> Effect Buffer
95-
fromString str = fromStringImpl str <<< encodingToNode
96-
97-
foreign import fromStringImpl :: String -> String -> Effect Buffer
98-
99-
foreign import toArrayBuffer :: Buffer -> Effect ArrayBuffer
100-
101-
-- | Reads a numeric value from a buffer at the specified offset.
102-
read :: BufferValueType -> Offset -> Buffer -> Effect Int
103-
read = readImpl <<< show
104-
105-
foreign import readImpl :: String -> Offset -> Buffer -> Effect Int
106-
107-
-- | Reads a section of a buffer as a string with the specified encoding.
108-
readString :: Encoding -> Offset -> Offset -> Buffer -> Effect String
109-
readString = readStringImpl <<< encodingToNode
110-
111-
foreign import readStringImpl ::
112-
String -> Offset -> Offset -> Buffer -> Effect String
113-
114-
-- | Reads the buffer as a string with the specified encoding.
115-
toString :: Encoding -> Buffer -> Effect String
116-
toString = toStringImpl <<< encodingToNode
117-
118-
foreign import toStringImpl :: String -> Buffer -> Effect String
119-
120-
-- | Writes a numeric value to a buffer at the specified offset.
121-
write :: BufferValueType -> Int -> Offset -> Buffer -> Effect Unit
122-
write = writeImpl <<< show
123-
124-
foreign import writeImpl :: String -> Int -> Offset -> Buffer -> Effect Unit
125-
126-
-- | Writes octets from a string to a buffer at the specified offset. Multi-byte
127-
-- | characters will not be written to the buffer if there is not enough capacity
128-
-- | to write them fully. The number of bytes written is returned.
129-
writeString :: Encoding -> Offset -> Int -> String -> Buffer -> Effect Int
130-
writeString = writeStringImpl <<< encodingToNode
131-
132-
foreign import writeStringImpl ::
133-
String -> Offset -> Int -> String -> Buffer -> Effect Int
134-
135-
-- | Creates an array of octets from a buffer's contents.
136-
foreign import toArray :: Buffer -> Effect (Array Octet)
137-
138-
-- | Reads an octet from a buffer at the specified offset.
139-
getAtOffset :: Offset -> Buffer -> Effect (Maybe Octet)
140-
getAtOffset = getAtOffsetImpl Just Nothing
141-
142-
foreign import getAtOffsetImpl ::
143-
(Octet -> Maybe Octet) -> Maybe Octet -> Offset -> Buffer -> Effect (Maybe Octet)
144-
145-
-- | Writes an octet in the buffer at the specified offset.
146-
foreign import setAtOffset :: Octet -> Offset -> Buffer -> Effect Unit
147-
148-
-- | Returns the size of a buffer.
149-
foreign import size :: Buffer -> Effect Int
150-
151-
-- | Concatenates a list of buffers.
152-
foreign import concat :: Array Buffer -> Effect Buffer
153-
154-
-- | Concatenates a list of buffers, combining them into a new buffer of the
155-
-- | specified length.
156-
foreign import concat' :: Array Buffer -> Int -> Effect Buffer
157-
158-
-- | Copies a section of a source buffer into a target buffer at the specified
159-
-- | offset, and returns the number of octets copied.
160-
foreign import copy :: Offset -> Offset -> Buffer -> Offset -> Buffer -> Effect Int
161-
162-
-- | Fills a range in a buffer with the specified octet.
163-
foreign import fill ::
164-
Octet -> Offset -> Offset -> Buffer -> Effect Unit
17+
instance mutableBufferEffect :: MutableBuffer Buffer Effect where
18+
create = Internal.create
19+
freeze = Internal.copyAll
20+
unsafeFreeze = Internal.unsafeFreeze
21+
thaw = Internal.copyAll
22+
unsafeThaw = Internal.unsafeThaw
23+
fromArray = Internal.fromArray
24+
fromString = Internal.fromString
25+
fromArrayBuffer = Internal.fromArrayBuffer
26+
toArrayBuffer = Internal.toArrayBuffer
27+
read = Internal.read
28+
readString = Internal.readString
29+
toString = Internal.toString
30+
write = Internal.write
31+
writeString = Internal.writeString
32+
toArray = Internal.toArray
33+
getAtOffset = Internal.getAtOffset
34+
setAtOffset = Internal.setAtOffset
35+
slice = Internal.slice
36+
size = Internal.size
37+
concat = Internal.concat
38+
concat' = Internal.concat'
39+
copy = Internal.copy
40+
fill = Internal.fill

‎src/Node/Buffer/Class.purs

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
module Node.Buffer.Class
2+
( class MutableBuffer
3+
, create
4+
, freeze
5+
, unsafeFreeze
6+
, thaw
7+
, unsafeThaw
8+
, fromArray
9+
, fromString
10+
, fromArrayBuffer
11+
, toArrayBuffer
12+
, read
13+
, readString
14+
, toString
15+
, write
16+
, writeString
17+
, toArray
18+
, getAtOffset
19+
, setAtOffset
20+
, slice
21+
, size
22+
, concat
23+
, concat'
24+
, copy
25+
, fill
26+
) where
27+
28+
import Prelude
29+
30+
import Data.ArrayBuffer.Types (ArrayBuffer)
31+
import Data.Maybe (Maybe)
32+
import Node.Buffer.Immutable (ImmutableBuffer)
33+
import Node.Buffer.Types (BufferValueType, Octet, Offset)
34+
import Node.Encoding (Encoding)
35+
36+
-- | A type class for mutable buffers `buf` where operations on those buffers are
37+
-- | represented by a particular monadic effect type `m`.
38+
class Monad m <= MutableBuffer buf m | buf -> m where
39+
40+
-- | Creates a new buffer of the specified size.
41+
create :: Int -> m buf
42+
43+
-- | Creates an immutable copy of a mutable buffer.
44+
freeze :: buf -> m ImmutableBuffer
45+
46+
-- | O(1). Convert a mutable buffer to an immutable buffer, without copying. The
47+
-- | mutable buffer must not be mutated afterwards.
48+
unsafeFreeze :: buf -> m ImmutableBuffer
49+
50+
-- | Creates a mutable copy of an immutable buffer.
51+
thaw :: ImmutableBuffer -> m buf
52+
53+
-- | O(1) Convert an immutable buffer to a mutable buffer, without copying. The
54+
-- | input buffer must not be used afterward.
55+
unsafeThaw :: ImmutableBuffer -> m buf
56+
57+
-- | Creates a new buffer from an array of octets, sized to match the array.
58+
fromArray :: Array Octet -> m buf
59+
60+
-- | Creates a new buffer from a string with the specified encoding, sized to
61+
-- | match the string.
62+
fromString :: String -> Encoding -> m buf
63+
64+
-- | Creates a buffer view from a JS ArrayByffer without copying data.
65+
fromArrayBuffer :: ArrayBuffer -> m buf
66+
67+
-- | Copies the data in the buffer to a new JS ArrayBuffer
68+
toArrayBuffer :: buf -> m ArrayBuffer
69+
70+
-- | Reads a numeric value from a buffer at the specified offset.
71+
read :: BufferValueType -> Offset -> buf -> m Int
72+
73+
-- | Reads a section of a buffer as a string with the specified encoding.
74+
readString :: Encoding -> Offset -> Offset -> buf -> m String
75+
76+
-- | Reads the buffer as a string with the specified encoding.
77+
toString :: Encoding -> buf -> m String
78+
79+
-- | Writes a numeric value to a buffer at the specified offset.
80+
write :: BufferValueType -> Int -> Offset -> buf -> m Unit
81+
82+
-- | Writes octets from a string to a buffer at the specified offset. Multi-byte
83+
-- | characters will not be written to the buffer if there is not enough capacity
84+
-- | to write them fully. The number of bytes written is returned.
85+
writeString :: Encoding -> Offset -> Int -> String -> buf -> m Int
86+
87+
-- | Creates an array of octets from a buffer's contents.
88+
toArray :: buf -> m (Array Octet)
89+
90+
-- | Reads an octet from a buffer at the specified offset.
91+
getAtOffset :: Offset -> buf -> m (Maybe Octet)
92+
93+
-- | Writes an octet in the buffer at the specified offset.
94+
setAtOffset :: Octet -> Offset -> buf -> m Unit
95+
96+
-- | Creates a new buffer slice that acts like a window on the original buffer.
97+
-- | Writing to the slice buffer updates the original buffer and vice-versa.
98+
slice :: Offset -> Offset -> buf -> buf
99+
100+
-- | Returns the size of a buffer.
101+
size :: buf -> m Int
102+
103+
-- | Concatenates a list of buffers.
104+
concat :: Array buf -> m buf
105+
106+
-- | Concatenates a list of buffers, combining them into a new buffer of the
107+
-- | specified length.
108+
concat' :: Array buf -> Int -> m buf
109+
110+
-- | Copies a section of a source buffer into a target buffer at the specified
111+
-- | offset, and returns the number of octets copied.
112+
copy :: Offset -> Offset -> buf -> Offset -> buf -> m Int
113+
114+
-- | Fills a range in a buffer with the specified octet.
115+
fill :: Octet -> Offset -> Offset -> buf -> m Unit

‎src/Node/Buffer/Immutable.js

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/* global exports */
2+
/* global Buffer */
3+
/* global require */
4+
"use strict";
5+
6+
exports.showImpl = require('util').inspect;
7+
8+
exports.eqImpl = function(a) {
9+
return function(b) {
10+
return a.equals(b);
11+
}
12+
};
13+
14+
exports.compareImpl = function(a) {
15+
return function (b) {
16+
return a.compare(b);
17+
};
18+
}
19+
20+
exports.create = function (size) {
21+
return Buffer.alloc(size);
22+
};
23+
24+
exports.fromArray = function (octets) {
25+
return Buffer.from(octets);
26+
};
27+
28+
exports.size = function (buff) {
29+
return buff.length;
30+
};
31+
32+
exports.toArray = function (buff) {
33+
var json = buff.toJSON()
34+
return json.data || json;
35+
};
36+
37+
exports.toArrayBuffer = function(buff) {
38+
return buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength);
39+
};
40+
41+
exports.fromArrayBuffer = function(ab) {
42+
return Buffer.from(ab);
43+
};
44+
45+
exports.fromStringImpl = function (str) {
46+
return function (encoding) {
47+
return Buffer.from(str, encoding);
48+
};
49+
};
50+
51+
exports.readImpl = function (ty) {
52+
return function (offset) {
53+
return function (buf) {
54+
return buf['read' + ty](offset);
55+
};
56+
};
57+
};
58+
59+
exports.readStringImpl = function (enc) {
60+
return function (start) {
61+
return function (end) {
62+
return function (buff) {
63+
return buff.toString(enc, start, end);
64+
};
65+
};
66+
};
67+
};
68+
69+
exports.getAtOffsetImpl = function (just) {
70+
return function (nothing) {
71+
return function (offset) {
72+
return function (buff) {
73+
var octet = buff[offset];
74+
return octet == null ? nothing
75+
: just(octet);
76+
};
77+
};
78+
};
79+
};
80+
81+
exports.toStringImpl = function (enc) {
82+
return function (buff) {
83+
return buff.toString(enc);
84+
};
85+
};
86+
87+
exports.slice = function (start) {
88+
return function (end) {
89+
return function (buff) {
90+
return buff.slice(start, end);
91+
};
92+
};
93+
};
94+
95+
exports.concat = function (buffs) {
96+
return Buffer.concat(buffs);
97+
};
98+
99+
exports["concat'"] = function (buffs) {
100+
return function (totalLength) {
101+
return Buffer.concat(buffs, totalLength);
102+
};
103+
};

‎src/Node/Buffer/Immutable.purs

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
-- | Immutable buffers and associated operations.
2+
module Node.Buffer.Immutable
3+
( ImmutableBuffer
4+
, create
5+
, fromArray
6+
, fromString
7+
, fromArrayBuffer
8+
, read
9+
, readString
10+
, toString
11+
, toArray
12+
, toArrayBuffer
13+
, getAtOffset
14+
, concat
15+
, concat'
16+
, slice
17+
, size
18+
) where
19+
20+
import Prelude
21+
22+
import Data.ArrayBuffer.Types (ArrayBuffer)
23+
import Data.Maybe (Maybe(..))
24+
import Node.Buffer.Types (BufferValueType, Octet, Offset)
25+
import Node.Encoding (Encoding, encodingToNode)
26+
27+
-- | An immutable buffer that exists independently of any memory region or effect.
28+
foreign import data ImmutableBuffer :: Type
29+
30+
instance showBuffer :: Show ImmutableBuffer where
31+
show = showImpl
32+
33+
foreign import showImpl :: ImmutableBuffer -> String
34+
35+
instance eqBuffer :: Eq ImmutableBuffer where
36+
eq = eqImpl
37+
38+
foreign import eqImpl :: ImmutableBuffer -> ImmutableBuffer -> Boolean
39+
40+
instance ordBuffer :: Ord ImmutableBuffer where
41+
compare a b =
42+
case compareImpl a b of
43+
x | x < 0 -> LT
44+
x | x > 0 -> GT
45+
otherwise -> EQ
46+
47+
foreign import compareImpl :: ImmutableBuffer -> ImmutableBuffer -> Int
48+
49+
-- | Creates a new buffer of the specified size.
50+
foreign import create :: Int -> ImmutableBuffer
51+
52+
-- | Creates a new buffer from an array of octets, sized to match the array.
53+
foreign import fromArray :: Array Octet -> ImmutableBuffer
54+
55+
-- | Creates a buffer view from a JS ArrayByffer without copying data.
56+
--
57+
-- Requires Node >= v5.10.0
58+
foreign import fromArrayBuffer :: ArrayBuffer -> ImmutableBuffer
59+
60+
-- | Creates a new buffer from a string with the specified encoding, sized to match the string.
61+
fromString :: String -> Encoding -> ImmutableBuffer
62+
fromString str = fromStringImpl str <<< encodingToNode
63+
64+
foreign import fromStringImpl :: String -> String -> ImmutableBuffer
65+
66+
-- | Reads a numeric value from a buffer at the specified offset.
67+
read :: BufferValueType -> Offset -> ImmutableBuffer -> Int
68+
read = readImpl <<< show
69+
70+
foreign import readImpl :: String -> Offset -> ImmutableBuffer -> Int
71+
72+
-- | Reads a section of a buffer as a string with the specified encoding.
73+
readString :: Encoding -> Offset -> Offset -> ImmutableBuffer -> String
74+
readString = readStringImpl <<< encodingToNode
75+
76+
foreign import readStringImpl ::
77+
String -> Offset -> Offset -> ImmutableBuffer -> String
78+
79+
-- | Reads the buffer as a string with the specified encoding.
80+
toString :: Encoding -> ImmutableBuffer -> String
81+
toString = toStringImpl <<< encodingToNode
82+
83+
foreign import toStringImpl :: String -> ImmutableBuffer -> String
84+
85+
-- | Creates an array of octets from a buffer's contents.
86+
foreign import toArray :: ImmutableBuffer -> Array Octet
87+
88+
-- | Creates an `ArrayBuffer` by copying a buffer's contents.
89+
foreign import toArrayBuffer :: ImmutableBuffer -> ArrayBuffer
90+
91+
-- | Reads an octet from a buffer at the specified offset.
92+
getAtOffset :: Offset -> ImmutableBuffer -> Maybe Octet
93+
getAtOffset = getAtOffsetImpl Just Nothing
94+
95+
foreign import getAtOffsetImpl ::
96+
(Octet -> Maybe Octet) -> Maybe Octet -> Offset -> ImmutableBuffer -> Maybe Octet
97+
98+
-- | Concatenates a list of buffers.
99+
foreign import concat :: Array ImmutableBuffer -> ImmutableBuffer
100+
101+
-- | Concatenates a list of buffers, combining them into a new buffer of the
102+
-- | specified length.
103+
foreign import concat' :: Array ImmutableBuffer -> Int -> ImmutableBuffer
104+
105+
-- | Creates a new buffer slice that shares the memory of the original buffer.
106+
foreign import slice :: Offset -> Offset -> ImmutableBuffer -> ImmutableBuffer
107+
108+
-- | Returns the size of a buffer.
109+
foreign import size :: ImmutableBuffer -> Int

‎src/Node/Buffer/Internal.js

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* global exports */
2+
/* global Buffer */
3+
"use strict";
4+
5+
exports.copyAll = function(a) {
6+
return function() {
7+
return Buffer.from(a);
8+
};
9+
};
10+
11+
exports.writeInternal = function (ty) {
12+
return function (value) {
13+
return function (offset) {
14+
return function (buf) {
15+
return function() {
16+
buf['write' + ty](value, offset);
17+
return {};
18+
}
19+
};
20+
};
21+
};
22+
};
23+
24+
exports.writeStringInternal = function (encoding) {
25+
return function (offset) {
26+
return function (length) {
27+
return function (value) {
28+
return function (buff) {
29+
return function() {
30+
return buff.write(value, offset, length, encoding);
31+
}
32+
};
33+
};
34+
};
35+
};
36+
};
37+
38+
exports.setAtOffset = function (value) {
39+
return function (offset) {
40+
return function (buff) {
41+
return function() {
42+
buff[offset] = value;
43+
return {};
44+
};
45+
};
46+
};
47+
};
48+
49+
exports.copy = function (srcStart) {
50+
return function (srcEnd) {
51+
return function (src) {
52+
return function (targStart) {
53+
return function (targ) {
54+
return function() {
55+
return src.copy(targ, targStart, srcStart, srcEnd);
56+
};
57+
};
58+
};
59+
};
60+
};
61+
};
62+
63+
exports.fill = function (octet) {
64+
return function (start) {
65+
return function (end) {
66+
return function (buf) {
67+
return function() {
68+
buf.fill(octet, start, end);
69+
return {};
70+
};
71+
};
72+
};
73+
};
74+
};

‎src/Node/Buffer/Internal.purs

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
-- | Functions and types to support the other modules. Not for public use.
2+
module Node.Buffer.Internal
3+
( unsafeFreeze
4+
, unsafeThaw
5+
, usingFromImmutable
6+
, usingToImmutable
7+
, create
8+
, copyAll
9+
, fromArray
10+
, fromString
11+
, fromArrayBuffer
12+
, toArrayBuffer
13+
, read
14+
, readString
15+
, toString
16+
, write
17+
, writeString
18+
, toArray
19+
, getAtOffset
20+
, setAtOffset
21+
, slice
22+
, size
23+
, concat
24+
, concat'
25+
, copy
26+
, fill ) where
27+
28+
import Prelude
29+
30+
import Data.ArrayBuffer.Types (ArrayBuffer)
31+
import Data.Maybe (Maybe)
32+
import Node.Buffer.Immutable (ImmutableBuffer)
33+
import Node.Buffer.Immutable as Immutable
34+
import Node.Buffer.Types (BufferValueType, Octet, Offset)
35+
import Node.Encoding (Encoding, encodingToNode)
36+
import Unsafe.Coerce (unsafeCoerce)
37+
38+
unsafeFreeze :: forall buf m. Monad m => buf -> m ImmutableBuffer
39+
unsafeFreeze = pure <<< unsafeCoerce
40+
41+
unsafeThaw :: forall buf m. Monad m => ImmutableBuffer -> m buf
42+
unsafeThaw = pure <<< unsafeCoerce
43+
44+
usingFromImmutable :: forall buf m a. Monad m => (ImmutableBuffer -> a) -> buf -> m a
45+
usingFromImmutable f buf = f <$> unsafeFreeze buf
46+
47+
usingToImmutable :: forall buf m a. Monad m => (a -> ImmutableBuffer) -> a -> m buf
48+
usingToImmutable f x = unsafeThaw $ f x
49+
50+
create :: forall buf m. Monad m => Int -> m buf
51+
create = usingToImmutable Immutable.create
52+
53+
foreign import copyAll :: forall a buf m. a -> m buf
54+
55+
fromArray :: forall buf m. Monad m => Array Octet -> m buf
56+
fromArray = usingToImmutable Immutable.fromArray
57+
58+
fromString :: forall buf m. Monad m => String -> Encoding -> m buf
59+
fromString s = usingToImmutable $ Immutable.fromString s
60+
61+
fromArrayBuffer :: forall buf m. Monad m => ArrayBuffer -> m buf
62+
fromArrayBuffer = usingToImmutable Immutable.fromArrayBuffer
63+
64+
toArrayBuffer :: forall buf m. Monad m => buf -> m ArrayBuffer
65+
toArrayBuffer = usingFromImmutable Immutable.toArrayBuffer
66+
67+
read :: forall buf m. Monad m => BufferValueType -> Offset -> buf -> m Int
68+
read t o = usingFromImmutable $ Immutable.read t o
69+
70+
readString :: forall buf m. Monad m => Encoding -> Offset -> Offset -> buf -> m String
71+
readString m o o' = usingFromImmutable $ Immutable.readString m o o'
72+
73+
toString :: forall buf m. Monad m => Encoding -> buf -> m String
74+
toString m = usingFromImmutable $ Immutable.toString m
75+
76+
write :: forall buf m. Monad m => BufferValueType -> Int -> Offset -> buf -> m Unit
77+
write = writeInternal <<< show
78+
79+
foreign import writeInternal :: forall buf m. String -> Int -> Offset -> buf -> m Unit
80+
81+
writeString :: forall buf m. Monad m => Encoding -> Offset -> Int -> String -> buf -> m Int
82+
writeString = writeStringInternal <<< encodingToNode
83+
84+
foreign import writeStringInternal ::
85+
forall buf m. String -> Offset -> Int -> String -> buf -> m Int
86+
87+
toArray :: forall buf m. Monad m => buf -> m (Array Octet)
88+
toArray = usingFromImmutable Immutable.toArray
89+
90+
getAtOffset :: forall buf m. Monad m => Offset -> buf -> m (Maybe Octet)
91+
getAtOffset o = usingFromImmutable $ Immutable.getAtOffset o
92+
93+
foreign import setAtOffset :: forall buf m. Octet -> Offset -> buf -> m Unit
94+
95+
slice :: forall buf. Offset -> Offset -> buf -> buf
96+
slice = unsafeCoerce Immutable.slice
97+
98+
size :: forall buf m. Monad m => buf -> m Int
99+
size = usingFromImmutable Immutable.size
100+
101+
concat :: forall buf m. Array buf -> m buf
102+
concat arrs = unsafeCoerce \_ -> Immutable.concat (unsafeCoerce arrs)
103+
104+
concat' :: forall buf m. Monad m => Array buf -> Int -> m buf
105+
concat' arrs n = unsafeCoerce \_ -> Immutable.concat' (unsafeCoerce arrs) n
106+
107+
foreign import copy :: forall buf m. Offset -> Offset -> buf -> Offset -> buf -> m Int
108+
109+
foreign import fill :: forall buf m. Octet -> Offset -> Offset -> buf -> m Unit

‎src/Node/Buffer/ST.purs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module Node.Buffer.ST
2+
( STBuffer
3+
, run
4+
) where
5+
6+
import Prelude
7+
8+
import Control.Monad.ST (ST, kind Region)
9+
import Control.Monad.ST as ST
10+
import Node.Buffer.Class (class MutableBuffer, unsafeFreeze)
11+
import Node.Buffer.Immutable (ImmutableBuffer)
12+
import Node.Buffer.Internal as Internal
13+
14+
-- | A reference to a mutable buffer for use with `ST`
15+
-- |
16+
-- | The type parameter represents the memory region which the buffer belongs to.
17+
foreign import data STBuffer :: Region -> Type
18+
19+
-- | Runs an effect creating an `STBuffer` then freezes the buffer and returns
20+
-- | it, without unneccessary copying.
21+
run :: (forall h. ST h (STBuffer h)) -> ImmutableBuffer
22+
run st = ST.run (st >>= unsafeFreeze)
23+
24+
instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where
25+
create = Internal.create
26+
freeze = Internal.copyAll
27+
unsafeFreeze = Internal.unsafeFreeze
28+
thaw = Internal.copyAll
29+
unsafeThaw = Internal.unsafeThaw
30+
fromArray = Internal.fromArray
31+
fromString = Internal.fromString
32+
fromArrayBuffer = Internal.fromArrayBuffer
33+
toArrayBuffer = Internal.toArrayBuffer
34+
read = Internal.read
35+
readString = Internal.readString
36+
toString = Internal.toString
37+
write = Internal.write
38+
writeString = Internal.writeString
39+
toArray = Internal.toArray
40+
getAtOffset = Internal.getAtOffset
41+
setAtOffset = Internal.setAtOffset
42+
slice = Internal.slice
43+
size = Internal.size
44+
concat = Internal.concat
45+
concat' = Internal.concat'
46+
copy = Internal.copy
47+
fill = Internal.fill

‎src/Node/Buffer/Types.purs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module Node.Buffer.Types
2+
( Octet
3+
, Offset
4+
, BufferValueType(..)
5+
) where
6+
7+
import Prelude
8+
9+
-- | Type synonym indicating the value should be an octet (0-255). If the value
10+
-- | provided is outside this range it will be used as modulo 256.
11+
type Octet = Int
12+
13+
-- | Type synonym indicating the value refers to an offset in a buffer.
14+
type Offset = Int
15+
16+
-- | Enumeration of the numeric types that can be written to a buffer.
17+
data BufferValueType
18+
= UInt8
19+
| UInt16LE
20+
| UInt16BE
21+
| UInt32LE
22+
| UInt32BE
23+
| Int8
24+
| Int16LE
25+
| Int16BE
26+
| Int32LE
27+
| Int32BE
28+
| FloatLE
29+
| FloatBE
30+
| DoubleLE
31+
| DoubleBE
32+
33+
instance showBufferValueType :: Show BufferValueType where
34+
show UInt8 = "UInt8"
35+
show UInt16LE = "UInt16LE"
36+
show UInt16BE = "UInt16BE"
37+
show UInt32LE = "UInt32LE"
38+
show UInt32BE = "UInt32BE"
39+
show Int8 = "Int8"
40+
show Int16LE = "Int16LE"
41+
show Int16BE = "Int16BE"
42+
show Int32LE = "Int32LE"
43+
show Int32BE = "Int32BE"
44+
show FloatLE = "FloatLE"
45+
show FloatBE = "FloatBE"
46+
show DoubleLE = "DoubleLE"
47+
show DoubleBE = "DoubleBE"

‎src/Node/Buffer/Unsafe.js

-11
This file was deleted.

‎src/Node/Buffer/Unsafe.purs

-10
This file was deleted.

‎test/Main.purs

-137
This file was deleted.

‎test/Test/Main.purs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module Test.Main where
2+
3+
import Prelude
4+
import Effect (Effect)
5+
import Test.Node.Buffer as Buffer
6+
7+
main :: Effect Unit
8+
main = Buffer.test

‎test/Test/Node/Buffer.purs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module Test.Node.Buffer (test) where
2+
3+
import Prelude
4+
5+
import Effect (Effect)
6+
import Effect.Console (log)
7+
import Node.Buffer (Buffer)
8+
import Test.Node.Buffer.Class (testMutableBuffer)
9+
import Test.Node.Buffer.Immutable as Immutable
10+
import Test.Node.Buffer.ST (test) as ST
11+
import Type.Proxy (Proxy(..))
12+
13+
test :: Effect Unit
14+
test = do
15+
16+
log "Testing Node.Buffer ..."
17+
testMutableBuffer (Proxy :: Proxy Buffer) identity
18+
19+
ST.test
20+
Immutable.test

‎test/Test/Node/Buffer/Class.purs

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
module Test.Node.Buffer.Class (testMutableBuffer) where
2+
3+
import Prelude
4+
5+
import Data.ArrayBuffer.Types (ArrayBuffer)
6+
import Data.Maybe (Maybe(..))
7+
import Data.Traversable (traverse)
8+
import Effect (Effect)
9+
import Effect.Console (log)
10+
import Node.Buffer (class MutableBuffer, BufferValueType(..), concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, fromString, getAtOffset, read, readString, setAtOffset, slice, thaw, toArray, toArrayBuffer, toString, write)
11+
import Node.Buffer.Immutable as Immutable
12+
import Node.Encoding (Encoding(..))
13+
import Test.Assert (assertEqual)
14+
import Type.Proxy (Proxy)
15+
16+
testMutableBuffer :: forall buf m. MutableBuffer buf m =>
17+
Proxy buf -> (forall a. m a -> Effect a) -> Effect Unit
18+
testMutableBuffer _ run = do
19+
20+
log " - create"
21+
testCreate
22+
23+
log " - freeze"
24+
testFreeze
25+
26+
log " - thaw"
27+
testThaw
28+
29+
log " - Reading and writing"
30+
testReadWrite
31+
32+
log " - fromArray"
33+
testFromArray
34+
35+
log " - toArray"
36+
testToArray
37+
38+
log " - fromString"
39+
testFromString
40+
41+
log " - (to/from)ArrayBuffer"
42+
testToFromArrayBuffer
43+
44+
log " - toString"
45+
testToString
46+
47+
log " - readString"
48+
testReadString
49+
50+
log " - slice"
51+
testSlice
52+
53+
log " - copy"
54+
testCopy
55+
56+
log " - fill"
57+
testFill
58+
59+
log " - concat'"
60+
testConcat'
61+
62+
log " - getAtOffset"
63+
testGetAtOffset
64+
65+
where
66+
testCreate :: Effect Unit
67+
testCreate = do
68+
buf <- run ((create 3 :: m buf) >>= toArray)
69+
assertEqual {expected: [0, 0, 0], actual: buf}
70+
71+
testFreeze :: Effect Unit
72+
testFreeze = do
73+
buf <- Immutable.toArray <$> run ((fromArray [1, 2, 3] :: m buf) >>= freeze)
74+
assertEqual {expected: [1, 2, 3], actual: buf}
75+
76+
testThaw :: Effect Unit
77+
testThaw = do
78+
buf <- run ((thaw (Immutable.fromArray [1, 2, 3]) :: m buf) >>= toArray)
79+
assertEqual {expected: [1, 2, 3], actual: buf}
80+
81+
testReadWrite :: Effect Unit
82+
testReadWrite = do
83+
let val = 42
84+
readVal <- run do
85+
buf <- create 1 :: m buf
86+
write UInt8 val 0 buf
87+
read UInt8 0 buf
88+
89+
assertEqual {expected: val, actual: readVal}
90+
91+
testFromArray :: Effect Unit
92+
testFromArray = do
93+
readVal <- run do
94+
buf <- fromArray [1,2,3,4,5] :: m buf
95+
read UInt8 2 buf
96+
97+
assertEqual {expected: 3, actual: readVal}
98+
99+
testToArray :: Effect Unit
100+
testToArray = do
101+
let val = [1,2,67,3,3,7,8,3,4,237]
102+
valOut <- run do
103+
buf <- fromArray val :: m buf
104+
toArray buf
105+
106+
assertEqual {expected: val, actual: valOut}
107+
108+
testFromString :: Effect Unit
109+
testFromString = do
110+
let str = "hello, world"
111+
val <- run do
112+
buf <- fromString str ASCII :: m buf
113+
read UInt8 6 buf
114+
115+
assertEqual {expected: 32, actual: val} -- ASCII space
116+
117+
testToFromArrayBuffer :: Effect Unit
118+
testToFromArrayBuffer = do
119+
buf <- run $
120+
fromArray [1, 2, 3]
121+
>>= (toArrayBuffer :: buf -> m ArrayBuffer)
122+
>>= (fromArrayBuffer :: ArrayBuffer -> m buf)
123+
>>= toArray
124+
assertEqual {expected: [1, 2, 3], actual: buf}
125+
126+
testToString :: Effect Unit
127+
testToString = do
128+
let str = "hello, world"
129+
strOut <- run do
130+
buf <- fromString str ASCII :: m buf
131+
toString ASCII buf
132+
133+
assertEqual {expected: str, actual: strOut}
134+
135+
testReadString :: Effect Unit
136+
testReadString = do
137+
let str = "hello, world"
138+
strOut <- run do
139+
buf <- fromString str ASCII :: m buf
140+
readString ASCII 7 12 buf
141+
142+
assertEqual {expected: "world", actual: strOut}
143+
144+
testSlice :: Effect Unit
145+
testSlice = do
146+
{bufArr, bufSliceArr} <- run do
147+
buf <- fromArray [1, 2, 3, 4] :: m buf
148+
let bufSlice = slice 1 3 buf
149+
setAtOffset 42 1 bufSlice
150+
bufArr <- toArray buf
151+
bufSliceArr <- toArray bufSlice
152+
pure {bufArr, bufSliceArr}
153+
154+
assertEqual {expected: [1, 2, 42, 4], actual: bufArr}
155+
assertEqual {expected: [2, 42], actual: bufSliceArr}
156+
157+
testCopy :: Effect Unit
158+
testCopy = do
159+
{copied, out} <- run do
160+
buf1 <- fromArray [1,2,3,4,5] :: m buf
161+
buf2 <- fromArray [10,9,8,7,6]
162+
copied <- copy 0 3 buf1 2 buf2
163+
out <- toArray buf2
164+
pure {copied, out}
165+
166+
assertEqual {expected: 3, actual: copied}
167+
assertEqual {expected: [10,9,1,2,3], actual: out}
168+
169+
testFill :: Effect Unit
170+
testFill = do
171+
out <- run do
172+
buf <- fromArray [1,1,1,1,1] :: m buf
173+
fill 42 2 4 buf
174+
toArray buf
175+
176+
assertEqual {expected: [1,1,42,42,1], actual: out}
177+
178+
testConcat' :: Effect Unit
179+
testConcat' = do
180+
out <- run do
181+
bufs <- traverse (fromArray :: Array Int -> m buf) $ map (\x -> [x, x+1, x+2]) [0,3,6,9,12]
182+
buf <- concat' bufs 15
183+
toArray buf
184+
185+
assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out}
186+
187+
testGetAtOffset :: Effect Unit
188+
testGetAtOffset = do
189+
{o1, o4, om1} <- run do
190+
buf <- fromArray [1, 2, 3, 4] :: m buf
191+
o1 <- getAtOffset 1 buf
192+
o4 <- getAtOffset 4 buf
193+
om1 <- getAtOffset (-1) buf
194+
pure {o1, o4, om1}
195+
196+
assertEqual {expected: Just 2, actual: o1}
197+
assertEqual {expected: Nothing, actual: o4}
198+
assertEqual {expected: Nothing, actual: om1}

‎test/Test/Node/Buffer/Immutable.purs

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
module Test.Node.Buffer.Immutable (test) where
2+
3+
import Prelude
4+
5+
import Data.Maybe (Maybe(..))
6+
import Effect (Effect)
7+
import Effect.Class.Console (log)
8+
import Node.Buffer.Immutable as Immutable
9+
import Node.Buffer.Immutable (ImmutableBuffer)
10+
import Node.Buffer.Types (BufferValueType(..))
11+
import Node.Encoding (Encoding(..))
12+
import Test.Assert (assertEqual, assertTrue)
13+
14+
test :: Effect Unit
15+
test = do
16+
log "Testing Node.Buffer.Immutable ..."
17+
18+
log " - show"
19+
testShow
20+
21+
log " - eq"
22+
testEq
23+
24+
log " - compare"
25+
testCompare
26+
27+
log " - create"
28+
testCreate
29+
30+
log " - fromString"
31+
testFromString
32+
33+
log " - toString"
34+
testToString
35+
36+
log " - toArray"
37+
testToArray
38+
39+
log " - readString"
40+
testReadString
41+
42+
log " - getAtOffset"
43+
testGetAtOffset
44+
45+
log " - (to/from)ArrayBuffer"
46+
testToFromArrayBuffer
47+
48+
log " - concat'"
49+
testConcat'
50+
51+
log " - slice"
52+
testSlice
53+
54+
log " - size"
55+
testSize
56+
57+
buffer123 :: ImmutableBuffer
58+
buffer123 = Immutable.fromArray [1, 2, 3]
59+
60+
testShow :: Effect Unit
61+
testShow = do
62+
assertEqual {expected: "<Buffer 01 02 03>", actual: show buffer123}
63+
64+
testEq :: Effect Unit
65+
testEq = do
66+
assertTrue $ buffer123 == buffer123
67+
assertTrue $ buffer123 == Immutable.fromArray [1, 2, 3]
68+
assertTrue $ buffer123 /= Immutable.fromArray [1, 2, 4]
69+
assertTrue $ buffer123 /= Immutable.fromArray [1, 2]
70+
71+
testCompare :: Effect Unit
72+
testCompare = do
73+
assertEqual {expected: EQ, actual: compare buffer123 buffer123}
74+
assertEqual {expected: LT, actual: compare buffer123 $ Immutable.fromArray [3, 2, 1]}
75+
assertEqual {expected: GT, actual: compare buffer123 $ Immutable.fromArray [0, 1, 2]}
76+
77+
testCreate :: Effect Unit
78+
testCreate = do
79+
assertEqual {expected: Immutable.fromArray [], actual: Immutable.create 0}
80+
assertEqual {expected: Immutable.fromArray [0, 0, 0], actual: Immutable.create 3}
81+
82+
testFromString :: Effect Unit
83+
testFromString = do
84+
let buf = Immutable.fromString "hello, world" ASCII
85+
assertEqual {expected: 32, actual: Immutable.read UInt8 6 buf}
86+
87+
testToString :: Effect Unit
88+
testToString = do
89+
let str = "hello, world"
90+
str' = Immutable.toString ASCII $ Immutable.fromString str ASCII
91+
assertEqual {expected: str, actual: str'}
92+
93+
testToArray :: Effect Unit
94+
testToArray = do
95+
assertEqual {expected: [1, 2, 3], actual: Immutable.toArray buffer123}
96+
97+
testReadString :: Effect Unit
98+
testReadString = do
99+
let str = "hello, world"
100+
str' = Immutable.readString ASCII 7 12 $ Immutable.fromString str ASCII
101+
assertEqual {expected: "world", actual: str'}
102+
103+
testGetAtOffset :: Effect Unit
104+
testGetAtOffset = do
105+
assertEqual {expected: Just 2, actual: Immutable.getAtOffset 1 buffer123}
106+
assertEqual {expected: Nothing, actual: Immutable.getAtOffset 99 buffer123}
107+
assertEqual {expected: Nothing, actual: Immutable.getAtOffset (-1) buffer123}
108+
109+
testToFromArrayBuffer :: Effect Unit
110+
testToFromArrayBuffer = do
111+
assertEqual {expected: buffer123, actual: Immutable.fromArrayBuffer $ Immutable.toArrayBuffer buffer123}
112+
113+
testConcat' :: Effect Unit
114+
testConcat' = do
115+
let bufs = map (\x -> Immutable.fromArray [x, x+1, x+2]) [0,3,6,9,12]
116+
buf = Immutable.concat' bufs 15
117+
out = Immutable.toArray buf
118+
119+
assertEqual {expected: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14], actual: out}
120+
121+
testSlice :: Effect Unit
122+
testSlice = do
123+
assertEqual {expected: buffer123, actual: Immutable.slice 0 3 buffer123}
124+
assertEqual {expected: buffer123, actual: Immutable.slice 0 4 buffer123}
125+
assertEqual {expected: Immutable.fromArray [2], actual: Immutable.slice 1 2 buffer123}
126+
127+
testSize :: Effect Unit
128+
testSize = do
129+
assertEqual {expected: 0, actual: Immutable.size $ Immutable.fromArray []}
130+
assertEqual {expected: 3, actual: Immutable.size buffer123}

‎test/Test/Node/Buffer/ST.purs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module Test.Node.Buffer.ST (test) where
2+
3+
import Prelude
4+
5+
import Control.Monad.ST (run) as ST
6+
import Effect (Effect)
7+
import Effect.Console (log)
8+
import Node.Buffer.Class (create)
9+
import Node.Buffer.Immutable as Immutable
10+
import Node.Buffer.ST (STBuffer, run)
11+
import Test.Assert (assertEqual)
12+
import Test.Node.Buffer.Class (testMutableBuffer)
13+
import Type.Proxy (Proxy(..))
14+
import Unsafe.Coerce (unsafeCoerce)
15+
16+
test :: Effect Unit
17+
test = do
18+
log "Testing Node.Buffer.ST ..."
19+
testMutableBuffer (Proxy :: Proxy (STBuffer _)) unsafeCoerce
20+
log " - run"
21+
testRun
22+
23+
testRun :: Effect Unit
24+
testRun = do
25+
let buf = Immutable.toArray $ run (create 3)
26+
assertEqual {expected: [0, 0, 0], actual: buf}

0 commit comments

Comments
 (0)
Please sign in to comment.