diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c3a618..1c96a5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,16 +10,16 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: purescript-contrib/setup-purescript@main with: purescript: "unstable" purs-tidy: "latest" - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v3 with: - node-version: "14" + node-version: "latest" - name: Install dependencies run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb6fa6..1137cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,19 @@ Breaking changes: ``` New features: +- Added the following APIs (#55 by @JordanMartinez) + + - `Buffer.alloc`, `Buffer.allocUnsafe`, `Buffer.allocUnsafeSlow` + - `Buffer.poolSize`, `Buffer.setPoolSize` + - `buffer.swap16`, `buffer.swap32`, `buffer.swap64` + - `buffer.compare`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#bufcomparetarget-targetstart-targetend-sourcestart-sourceend + - `buffer.toString(encoding, start, end)`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#buftostringencoding-start-end + - `buffer.transcode(buf, from, to)` + - constants: + - `INSPECT_MAX_BYTES`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#bufferinspect_max_bytes + - `MAX_LENGTH`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#bufferconstantsmax_length + - `MAX_STRING_LENGTH`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#bufferconstantsmax_string_length + Bugfixes: @@ -51,6 +64,8 @@ Other improvements: - Format code with `purs-tidy`; enforce in CI (#52 by @JordanMartinez) - Update FFI to use uncurried functions (#54 by @JordanMartinez) - Removed `Internal.purs` file (#54 by @JordanMartinez) +- Bumped CI's node version to `latest` (#55 by @JordanMartinez) +- Updated CI `actions/checkout` and `actions/setup-nodee` to `v3` (#55 by @JordanMartinez) ## [v8.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v8.0.0) - 2022-04-27 diff --git a/src/Node/Buffer.js b/src/Node/Buffer.js index 8061884..8e56130 100644 --- a/src/Node/Buffer.js +++ b/src/Node/Buffer.js @@ -1,4 +1,7 @@ -import { Buffer } from "node:buffer"; +import { Buffer, transcode } from "node:buffer"; + +export const allocUnsafeImpl = (size) => Buffer.allocUnsafe(size); +export const allocUnsafeSlowImpl = (size) => Buffer.allocUnsafeSlow(size); export const freezeImpl = (a) => Buffer.from(a); export const thawImpl = (a) => Buffer.from(a); @@ -17,3 +20,14 @@ export const copyImpl = (srcStart, srcEnd, src, targStart, targ) => export const fillImpl = (octet, start, end, buf) => buf.fill(octet, start, end); + +export const poolSize = () => Buffer.poolSize; + +export const setPoolSizeImpl = (size) => { + Buffer.poolSize = size; +}; + +export const swap16Impl = (buf) => buf.swap16(); +export const swap32Impl = (buf) => buf.swap32(); +export const swap64Impl = (buf) => buf.swap64(); +export const transcodeImpl = (buf, from, to) => transcode(buf, from, to); diff --git a/src/Node/Buffer.purs b/src/Node/Buffer.purs index a6fe78f..e69f880 100644 --- a/src/Node/Buffer.purs +++ b/src/Node/Buffer.purs @@ -3,6 +3,10 @@ module Node.Buffer ( Buffer , module TypesExports , create + , alloc + , allocUnsafe + , allocUnsafeSlow + , compareParts , freeze , unsafeFreeze , thaw @@ -14,6 +18,7 @@ module Node.Buffer , read , readString , toString + , toString' , write , writeString , toArray @@ -25,6 +30,12 @@ module Node.Buffer , concat' , copy , fill + , poolSize + , setPoolSize + , swap16 + , swap32 + , swap64 + , transcode ) where import Prelude @@ -81,8 +92,29 @@ usingFromImmutable f buf = f <$> unsafeFreeze buf usingToImmutable :: forall a. (a -> ImmutableBuffer) -> a -> Effect Buffer usingToImmutable f x = unsafeThaw $ f x +-- | Creates a new buffer of the specified size. Alias to `alloc`. create :: Int -> Effect Buffer -create = usingToImmutable Immutable.create +create = alloc + +-- | Creates a new buffer of the specified size. +alloc :: Int -> Effect Buffer +alloc = usingToImmutable Immutable.alloc + +-- | Creates a new buffer of the specified size. Unsafe because it reuses memory from a pool +-- | and may contain sensitive data. See the Node docs: +-- | https://nodejs.org/docs/latest-v18.x/api/buffer.html#what-makes-bufferallocunsafe-and-bufferallocunsafeslow-unsafe +allocUnsafe :: Int -> Effect Buffer +allocUnsafe s = runEffectFn1 allocUnsafeImpl s + +foreign import allocUnsafeImpl :: EffectFn1 (Int) (Buffer) + +-- | Creates a new buffer of the specified size. Unsafe because it reuses memory from a pool +-- | and may contain sensitive data. See the Node docs: +-- | https://nodejs.org/docs/latest-v18.x/api/buffer.html#what-makes-bufferallocunsafe-and-bufferallocunsafeslow-unsafe +allocUnsafeSlow :: Int -> Effect Buffer +allocUnsafeSlow s = runEffectFn1 allocUnsafeSlowImpl s + +foreign import allocUnsafeSlowImpl :: EffectFn1 (Int) (Buffer) freeze :: Buffer -> Effect ImmutableBuffer freeze = runEffectFn1 freezeImpl @@ -106,6 +138,12 @@ fromArrayBuffer = usingToImmutable Immutable.fromArrayBuffer toArrayBuffer :: Buffer -> Effect ArrayBuffer toArrayBuffer = usingFromImmutable Immutable.toArrayBuffer +compareParts :: Buffer -> Buffer -> Offset -> Offset -> Offset -> Offset -> Effect Ordering +compareParts src target targetSrc targetEnd srcStart srcEnd = do + src' <- unsafeFreeze src + target' <- unsafeFreeze target + Immutable.compareParts src' target' targetSrc targetEnd srcStart srcEnd + read :: BufferValueType -> Offset -> Buffer -> Effect Number read t o = usingFromImmutable $ Immutable.read t o @@ -115,6 +153,9 @@ readString enc o o' = usingFromImmutable $ Immutable.readString enc o o' toString :: Encoding -> Buffer -> Effect String toString enc = usingFromImmutable $ Immutable.toString enc +toString' :: Encoding -> Offset -> Offset -> Buffer -> Effect String +toString' enc start end = usingFromImmutable $ Immutable.toString' enc start end + write :: BufferValueType -> Number -> Offset -> Buffer -> Effect Unit write ty value offset buf = runEffectFn4 writeInternal (show ty) value offset buf @@ -160,3 +201,31 @@ fill octet start end buf = do runEffectFn4 fillImpl octet start end buf foreign import fillImpl :: EffectFn4 Octet Offset Offset Buffer Unit + +-- | The size (in bytes) of pre-allocated internal Buffer instances used for pooling. This value may be modified. +foreign import poolSize :: Effect (Int) + +setPoolSize :: Int -> Effect Unit +setPoolSize sizeInBytes = runEffectFn1 setPoolSizeImpl sizeInBytes + +foreign import setPoolSizeImpl :: EffectFn1 (Int) (Unit) + +swap16 :: Buffer -> Effect Buffer +swap16 b = runEffectFn1 swap16Impl b + +foreign import swap16Impl :: EffectFn1 (Buffer) (Buffer) + +swap32 :: Buffer -> Effect Buffer +swap32 b = runEffectFn1 swap32Impl b + +foreign import swap32Impl :: EffectFn1 (Buffer) (Buffer) + +swap64 :: Buffer -> Effect Buffer +swap64 b = runEffectFn1 swap64Impl b + +foreign import swap64Impl :: EffectFn1 (Buffer) (Buffer) + +transcode :: Buffer -> Encoding -> Encoding -> Effect Buffer +transcode buf from to = runEffectFn3 transcodeImpl buf (encodingToNode from) (encodingToNode to) + +foreign import transcodeImpl :: EffectFn3 (Buffer) (String) (String) (Buffer) diff --git a/src/Node/Buffer/Constants.js b/src/Node/Buffer/Constants.js new file mode 100644 index 0000000..12bbaa0 --- /dev/null +++ b/src/Node/Buffer/Constants.js @@ -0,0 +1,5 @@ +import buffer from "node:buffer"; + +export const inspectMaxBytes = () => buffer.INSPECT_MAX_LENGTH; +export const maxLength = buffer.constants.MAX_LENGTH; +export const maxStringLength = buffer.constants.MAX_STRING_LENGTH; diff --git a/src/Node/Buffer/Constants.purs b/src/Node/Buffer/Constants.purs new file mode 100644 index 0000000..91b628b --- /dev/null +++ b/src/Node/Buffer/Constants.purs @@ -0,0 +1,9 @@ +module Node.Buffer.Constants where + +import Effect (Effect) + +foreign import inspectMaxBytes :: Effect Int + +foreign import maxLength :: Int + +foreign import maxStringLength :: Int diff --git a/src/Node/Buffer/Immutable.js b/src/Node/Buffer/Immutable.js index 4dd1c00..aa7eb7a 100644 --- a/src/Node/Buffer/Immutable.js +++ b/src/Node/Buffer/Immutable.js @@ -7,7 +7,10 @@ export const eqImpl = (a, b) => a.equals(b); export const compareImpl = (a, b) => a.compare(b); -export const create = (size) => Buffer.alloc(size); +export const comparePartsImpl = (src, target, targetStart, targetEnd, sourceStart, sourceEnd) => + src.compare(target, targetStart, targetEnd, sourceStart, sourceEnd); + +export const alloc = (size) => Buffer.alloc(size); export const fromArray = (octets) => Buffer.from(octets); @@ -33,6 +36,8 @@ export const getAtOffsetImpl = (offset, buff) => buff[offset]; export const toStringImpl = (enc, buff) => buff.toString(enc); +export const toStringSubImpl = (enc, start, end, buff) => buff.toString(enc, start, end); + export const sliceImpl = (start, end, buff) => buff.slice(start, end); export const concat = (buffs) => Buffer.concat(buffs); diff --git a/src/Node/Buffer/Immutable.purs b/src/Node/Buffer/Immutable.purs index 285c32c..c9e2aa0 100644 --- a/src/Node/Buffer/Immutable.purs +++ b/src/Node/Buffer/Immutable.purs @@ -1,13 +1,16 @@ -- | Immutable buffers and associated operations. module Node.Buffer.Immutable ( ImmutableBuffer + , compareParts , create + , alloc , fromArray , fromString , fromArrayBuffer , read , readString , toString + , toString' , toArray , toArrayBuffer , getAtOffset @@ -23,8 +26,11 @@ import Data.ArrayBuffer.Types (ArrayBuffer) import Data.Function.Uncurried (Fn2, Fn3, Fn4, runFn2, runFn3, runFn4) import Data.Maybe (Maybe) import Data.Nullable (Nullable, toMaybe) +import Effect (Effect) +import Effect.Uncurried (EffectFn6, runEffectFn6) import Node.Buffer.Types (BufferValueType, Octet, Offset) import Node.Encoding (Encoding, encodingToNode) +import Partial.Unsafe (unsafeCrashWith) -- | An immutable buffer that exists independently of any memory region or effect. foreign import data ImmutableBuffer :: Type @@ -48,23 +54,35 @@ instance ordBuffer :: Ord ImmutableBuffer where foreign import compareImpl :: Fn2 ImmutableBuffer ImmutableBuffer Int +-- | Creates a new buffer of the specified size. Alias for `alloc`. +create :: Int -> ImmutableBuffer +create = alloc + -- | Creates a new buffer of the specified size. -foreign import create :: Int -> ImmutableBuffer +foreign import alloc :: 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 = runFn2 fromStringImpl str <<< encodingToNode +fromString str enc = runFn2 fromStringImpl str $ encodingToNode enc foreign import fromStringImpl :: Fn2 String String ImmutableBuffer +compareParts :: ImmutableBuffer -> ImmutableBuffer -> Offset -> Offset -> Offset -> Offset -> Effect Ordering +compareParts src target targetStart targetEnd sourceStart sourceEnd = + runEffectFn6 comparePartsImpl src target targetStart targetEnd sourceStart sourceEnd <#> case _ of + -1 -> LT + 0 -> EQ + 1 -> GT + x -> unsafeCrashWith $ "Impossible: Invalid value: " <> show x + +foreign import comparePartsImpl :: EffectFn6 ImmutableBuffer ImmutableBuffer Int Int Int Int Int + -- | Reads a numeric value from a buffer at the specified offset. read :: BufferValueType -> Offset -> ImmutableBuffer -> Number read ty off buf = runFn3 readImpl (show ty) off buf @@ -83,6 +101,11 @@ toString enc buf = runFn2 toStringImpl (encodingToNode enc) buf foreign import toStringImpl :: Fn2 String ImmutableBuffer String +toString' :: Encoding -> Offset -> Offset -> ImmutableBuffer -> String +toString' enc start end buf = runFn4 toStringSubImpl enc start end buf + +foreign import toStringSubImpl :: Fn4 Encoding Offset Offset ImmutableBuffer String + -- | Creates an array of octets from a buffer's contents. foreign import toArray :: ImmutableBuffer -> Array Octet diff --git a/src/Node/Buffer/ST.purs b/src/Node/Buffer/ST.purs index 80e1d9d..429bb0c 100644 --- a/src/Node/Buffer/ST.purs +++ b/src/Node/Buffer/ST.purs @@ -2,6 +2,10 @@ module Node.Buffer.ST ( STBuffer , run , create + , alloc + , allocUnsafe + , allocUnsafeSlow + , compareParts , freeze , unsafeFreeze , thaw @@ -13,6 +17,7 @@ module Node.Buffer.ST , read , readString , toString + , toString' , write , writeString , toArray @@ -24,6 +29,10 @@ module Node.Buffer.ST , concat' , copy , fill + , swap16 + , swap32 + , swap64 + , transcode ) where import Prelude @@ -32,19 +41,19 @@ import Control.Monad.ST (ST, Region) import Control.Monad.ST as ST import Data.ArrayBuffer.Types (ArrayBuffer) import Data.Maybe (Maybe) -import Node.Buffer (BufferValueType) +import Node.Buffer (BufferValueType, Octet, Offset) import Node.Buffer as Buffer import Node.Buffer.Class (class MutableBuffer) import Node.Buffer.Immutable (ImmutableBuffer) import Node.Encoding (Encoding) import Unsafe.Coerce (unsafeCoerce) --- | A reference to a mutable buffer for use with `ST` +-- | A reference to a mutable Buffer for use with `ST` -- | --- | The type parameter represents the memory region which the buffer belongs to. +-- | The type parameter represents the memory region which the (STBuffer h) belongs to. foreign import data STBuffer :: Region -> Type --- | Runs an effect creating an `STBuffer` then freezes the buffer and returns +-- | Runs an ST creating an `STBuffer` then freezes the (STBuffer h) and returns -- | it, without unneccessary copying. run :: (forall h. ST h (STBuffer h)) -> ImmutableBuffer run st = ST.run (st >>= unsafeFreeze) @@ -74,10 +83,30 @@ instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where copy = copy fill = fill +-- | Creates a new `STBuffer`. Alias of `alloc`. create :: forall h. Int -> ST h (STBuffer h) -create = unsafeCoerce Buffer.create +create = alloc -freeze :: forall h. STBuffer h -> ST h ImmutableBuffer +-- | Creates a new `STBuffer`. +alloc :: forall h. Int -> ST h (STBuffer h) +alloc = unsafeCoerce Buffer.alloc + +-- | Creates a new `STBuffer` of the specified size. Unsafe because it reuses memory from a pool +-- | and may contain sensitive data. See the Node docs: +-- | https://nodejs.org/docs/latest-v18.x/api/buffer.html#what-makes-bufferallocunsafe-and-bufferallocunsafeslow-unsafe +allocUnsafe :: forall h. Int -> ST h (STBuffer h) +allocUnsafe = unsafeCoerce Buffer.allocUnsafe + +-- | Creates a new `STBuffer` of the specified size. Unsafe because it reuses memory from a pool +-- | and may contain sensitive data. See the Node docs: +-- | https://nodejs.org/docs/latest-v18.x/api/buffer.html#what-makes-bufferallocunsafe-and-bufferallocunsafeslow-unsafe +allocUnsafeSlow :: forall h. Int -> ST h (STBuffer h) +allocUnsafeSlow = unsafeCoerce Buffer.allocUnsafeSlow + +compareParts :: forall h. STBuffer h -> STBuffer h -> Int -> Int -> Int -> Int -> ST h Ordering +compareParts = unsafeCoerce Buffer.compareParts + +freeze :: forall h. (STBuffer h) -> ST h ImmutableBuffer freeze = unsafeCoerce Buffer.freeze unsafeFreeze :: forall h. STBuffer h -> ST h ImmutableBuffer @@ -89,7 +118,7 @@ thaw = unsafeCoerce Buffer.thaw unsafeThaw :: forall h. ImmutableBuffer -> ST h (STBuffer h) unsafeThaw = unsafeCoerce Buffer.unsafeThaw -fromArray :: forall h. Array Int -> ST h (STBuffer h) +fromArray :: forall h. Array Octet -> ST h (STBuffer h) fromArray = unsafeCoerce Buffer.fromArray fromString :: forall h. String -> Encoding -> ST h (STBuffer h) @@ -101,31 +130,35 @@ fromArrayBuffer = unsafeCoerce Buffer.fromArrayBuffer toArrayBuffer :: forall h. STBuffer h -> ST h ArrayBuffer toArrayBuffer = unsafeCoerce Buffer.toArrayBuffer -read :: forall h. BufferValueType -> Int -> STBuffer h -> ST h Number +read :: forall h. BufferValueType -> Offset -> STBuffer h -> ST h Number read = unsafeCoerce Buffer.read -readString :: forall h. Encoding -> Int -> Int -> STBuffer h -> ST h String +readString :: forall h. Encoding -> Offset -> Offset -> STBuffer h -> ST h String readString = unsafeCoerce Buffer.readString toString :: forall h. Encoding -> STBuffer h -> ST h String toString = unsafeCoerce Buffer.toString -write :: forall h. BufferValueType -> Number -> Int -> STBuffer h -> ST h Unit +toString' :: forall h. Encoding -> Offset -> Offset -> STBuffer h -> ST h String +toString' = unsafeCoerce Buffer.toString' + +write :: forall h. BufferValueType -> Number -> Offset -> STBuffer h -> ST h Unit write = unsafeCoerce Buffer.write -writeString :: forall h. Encoding -> Int -> Int -> String -> STBuffer h -> ST h Int -writeString = unsafeCoerce Buffer.writeString +writeString :: forall h. Encoding -> Offset -> Int -> String -> STBuffer h -> ST h Int +writeString = + unsafeCoerce Buffer.writeString -toArray :: forall h. STBuffer h -> ST h (Array Int) +toArray :: forall h. STBuffer h -> ST h (Array Octet) toArray = unsafeCoerce Buffer.toArray -getAtOffset :: forall h. Int -> STBuffer h -> ST h (Maybe Int) +getAtOffset :: forall h. Offset -> STBuffer h -> ST h (Maybe Octet) getAtOffset = unsafeCoerce Buffer.getAtOffset -setAtOffset :: forall h. Int -> Int -> STBuffer h -> ST h Unit +setAtOffset :: forall h. Octet -> Offset -> STBuffer h -> ST h Unit setAtOffset = unsafeCoerce Buffer.setAtOffset -slice :: forall h. Int -> Int -> STBuffer h -> STBuffer h +slice :: forall h. Offset -> Offset -> STBuffer h -> STBuffer h slice = unsafeCoerce Buffer.slice size :: forall h. STBuffer h -> ST h Int @@ -137,8 +170,20 @@ concat = unsafeCoerce Buffer.concat concat' :: forall h. Array (STBuffer h) -> Int -> ST h (STBuffer h) concat' = unsafeCoerce Buffer.concat' -copy :: forall h. Int -> Int -> STBuffer h -> Int -> STBuffer h -> ST h Int +copy :: forall h. Offset -> Offset -> STBuffer h -> Offset -> STBuffer h -> ST h Int copy = unsafeCoerce Buffer.copy -fill :: forall h. Int -> Int -> Int -> STBuffer h -> ST h Unit +fill :: forall h. Octet -> Offset -> Offset -> STBuffer h -> ST h Unit fill = unsafeCoerce Buffer.fill + +swap16 :: forall h. STBuffer h -> ST h (STBuffer h) +swap16 = unsafeCoerce Buffer.swap16 + +swap32 :: forall h. STBuffer h -> ST h (STBuffer h) +swap32 = unsafeCoerce Buffer.swap32 + +swap64 :: forall h. STBuffer h -> ST h (STBuffer h) +swap64 = unsafeCoerce Buffer.swap64 + +transcode :: forall h. STBuffer h -> Encoding -> Encoding -> ST h (STBuffer h) +transcode = unsafeCoerce Buffer.transcode