├── .eslintrc.json ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── package.json ├── src └── Node │ ├── Buffer.js │ ├── Buffer.purs │ ├── Buffer │ ├── Class.purs │ ├── Constants.js │ ├── Constants.purs │ ├── Immutable.js │ ├── Immutable.purs │ ├── ST.purs │ └── Types.purs │ ├── Encoding.js │ └── Encoding.purs └── test └── Test ├── Main.purs └── Node ├── Buffer.purs └── Buffer ├── Class.purs ├── Immutable.purs └── ST.purs /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module" 5 | }, 6 | "extends": "eslint:recommended", 7 | "rules": { 8 | "strict": [2, "global"], 9 | "block-scoped-var": 2, 10 | "consistent-return": 2, 11 | "eqeqeq": [2, "smart"], 12 | "guard-for-in": 2, 13 | "no-caller": 2, 14 | "no-extend-native": 2, 15 | "no-loop-func": 2, 16 | "no-new": 2, 17 | "no-param-reassign": 2, 18 | "no-return-assign": 2, 19 | "no-unused-expressions": 2, 20 | "no-use-before-define": 2, 21 | "radix": [2, "always"], 22 | "indent": [2, 2], 23 | "quotes": [2, "double"], 24 | "semi": [2, "always"] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Description of the change** 2 | 3 | Clearly and concisely describe the purpose of the pull request. If this PR relates to an existing issue or change proposal, please link to it. Include any other background context that would help reviewers understand the motivation for this PR. 4 | 5 | --- 6 | 7 | **Checklist:** 8 | 9 | - [ ] Added the change to the changelog's "Unreleased" section with a reference to this PR (e.g. "- Made a change (#0000)") 10 | - [ ] Linked any existing issues or proposals that this pull request should close 11 | - [ ] Updated or added relevant documentation 12 | - [ ] Added a test for the contribution (if applicable) 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - uses: purescript-contrib/setup-purescript@main 16 | with: 17 | purescript: "unstable" 18 | purs-tidy: "latest" 19 | 20 | - uses: actions/setup-node@v3 21 | with: 22 | node-version: "lts/*" 23 | 24 | - name: Install dependencies 25 | run: | 26 | npm install -g bower 27 | npm install 28 | bower install --production 29 | 30 | - name: Build source 31 | run: npm run-script build 32 | 33 | - name: Run tests 34 | run: | 35 | bower install 36 | npm run-script test --if-present 37 | 38 | - name: Check formatting 39 | run: | 40 | purs-tidy check src test 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.* 2 | !/.gitignore 3 | !/.eslintrc.json 4 | !/.github/ 5 | /bower_components/ 6 | /node_modules/ 7 | /output/ 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 4 | 5 | ## [Unreleased] 6 | 7 | Breaking changes: 8 | 9 | New features: 10 | 11 | Bugfixes: 12 | 13 | Other improvements: 14 | 15 | 16 | ## [v9.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v9.0.0) - 2022-06-05 17 | 18 | Breaking changes: 19 | - Expose Buffer API using typeclass-less API witout removing typeclass API (#53 by @JordanMartinez) 20 | 21 | Previously, compiler would fail to infer the type of `Buffer.create 1` as `Effect Buffer` 22 | because the Buffer API was exposed only via the multiparameter typeclass `MonadBuffer`. 23 | Due to the functional dependency between the two parameters, the monadic type cannot be inferred 24 | until the buffer type is known (either `Buffer` or `STBuffer`).: 25 | ```purs 26 | import Node.Buffer as Buffer 27 | 28 | -- Example 1 29 | main :: Effect Unit 30 | main = do 31 | x <- Buffer.create 1 -- compiler: is this `Int -> Effect Buffer` or `Int -> ST h (STBuffer h)? 32 | pure unit 33 | ``` 34 | 35 | The workaround was to add a type annotation, indicating the `x` is a `Buffer`: 36 | ```purs 37 | import Node.Buffer as Buffer 38 | 39 | -- Example 2 40 | main :: Effect Unit 41 | main = do 42 | x :: Buffer <- Buffer.create 1 -- compiler: Oh! It's `Int -> Effect Buffer` 43 | pure unit 44 | ``` 45 | 46 | This change does not break anyone's code if one was using a `create` (or another such typeclass member) 47 | to get `Int -> Effect Buffer`. Rather, such users can now drop the `:: Buffer` annotation 48 | (i.e. Example 1 above now compiles). 49 | 50 | If one was using `create` to get `forall m buf. MonadBuffer buf m => Int -> m buf`, 51 | then one will need to update their imports: 52 | ```diff 53 | -import Node.Buffer (class MonadBuffer) 54 | +import Node.Buffer.Class (class MonadBuffer) 55 | ``` 56 | 57 | New features: 58 | - Added the following APIs (#55 by @JordanMartinez) 59 | 60 | - `Buffer.alloc`, `Buffer.allocUnsafe`, `Buffer.allocUnsafeSlow` 61 | - `Buffer.poolSize`, `Buffer.setPoolSize` 62 | - `buffer.swap16`, `buffer.swap32`, `buffer.swap64` 63 | - `buffer.compare`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#bufcomparetarget-targetstart-targetend-sourcestart-sourceend 64 | - `buffer.toString(encoding, start, end)`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#buftostringencoding-start-end 65 | - `buffer.transcode(buf, from, to)` 66 | - constants: 67 | - `INSPECT_MAX_BYTES`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#bufferinspect_max_bytes 68 | - `MAX_LENGTH`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#bufferconstantsmax_length 69 | - `MAX_STRING_LENGTH`: https://nodejs.org/docs/latest-v18.x/api/buffer.html#bufferconstantsmax_string_length 70 | - Added a new data constructor for `Encoding`: `Base64Url` (#56 by @JordanMartinez) 71 | 72 | Bugfixes: 73 | 74 | Other improvements: 75 | - Format code with `purs-tidy`; enforce in CI (#52 by @JordanMartinez) 76 | - Update FFI to use uncurried functions (#54 by @JordanMartinez) 77 | - Removed `Internal.purs` file (#54 by @JordanMartinez) 78 | - Bumped CI's node version to `lts/*` (#55/#57 by @JordanMartinez) 79 | - Updated CI `actions/checkout` and `actions/setup-nodee` to `v3` (#55 by @JordanMartinez) 80 | 81 | ## [v8.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v8.0.0) - 2022-04-27 82 | 83 | Breaking changes: 84 | 85 | - Update project and deps to PureScript v0.15.0 (#46 by @sigma-andex and @JordanMartinez) 86 | 87 | New features: 88 | 89 | Bugfixes: 90 | 91 | Other improvements: 92 | 93 | ## [v7.0.1](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v7.0.1) - 2021-05-06 94 | 95 | Other improvements: 96 | - Fix warnings revealed by v0.14.1 PS release (#44 by @JordanMartinez) 97 | 98 | ## [v7.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v7.0.0) - 2021-02-26 99 | 100 | Breaking changes: 101 | - Updated code for PureScript 0.14 and dropped the `proxy` dependency as the `proxy` library has been migrated into `prelude` (#39). 102 | 103 | Other improvements: 104 | - Removed primes from the `concat'` function in FFI in preparation for ES modules support (#36) 105 | - Migrated CI to use GitHub Actions and updated installation instructions to use Spago (#38) 106 | - Stopped returning empty objects in foreign implementations for functions which return `Unit` for a small performance benefit (#40) 107 | - Added a CHANGELOG.md file and pull request template to the repository (#41) 108 | 109 | ## [v6.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v6.0.0) - 2019-07-24 110 | 111 | * Add a `MutableBuffer` type class with instances for both `Effect` and `ST`, to allow mutating buffers in either monad, and potentially other monads too (#24, @Dretch) 112 | * Remove the `Show Buffer` instance, as reading from a mutable buffer is effectful (@Dretch) 113 | * Use `Number` for reading and writing with Buffers (#25, @hdgarrood) 114 | 115 | ## [v5.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v5.0.0) - 2018-05-26 116 | 117 | - Updated for PureScript 0.12 118 | 119 | ## [v4.1.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v4.1.0) - 2017-12-11 120 | 121 | - Added `fromArrayBuffer` (@matthewleon) 122 | 123 | ## [v4.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v4.0.0) - 2017-11-19 124 | 125 | - Added `toArrayBuffer` (@matthewleon) 126 | 127 | ## [v3.0.1](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v3.0.1) - 2017-06-20 128 | 129 | - Fixed an encoding issue in `writeString` (@justinwoo) 130 | 131 | ## [v3.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v3.0.0) - 2017-04-04 132 | 133 | - Updated for 0.11 (@anilanar) 134 | 135 | ## [v2.0.1](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v2.0.1) - 2017-02-10 136 | 137 | - Fixed `getAtOffset` FFI implementation (@rightfold) 138 | - Fixed documentation typo (@rightfold) 139 | 140 | ## [v2.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v2.0.0) - 2016-10-17 141 | 142 | - Updated dependencies (@nwolverson) 143 | - Added `latin1` encoding (@Risto-Stevcev) 144 | 145 | ## [v1.0.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v1.0.0) - 2016-06-06 146 | 147 | - Compatibility with psc 0.9 148 | - Better behaved `Show` instance, plus an `encodingToNode` function which is now recommended for use with node APIs instead of `show`. 149 | 150 | ## [v0.2.2](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v0.2.2) - 2016-04-03 151 | 152 | - Fixed `byteLength`, which had a bad FFI declaration, so that it would throw an error every time it was called. 153 | 154 | ## [v0.2.1](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v0.2.1) - 2016-04-03 155 | 156 | - No code changes, this tag was just for publishing to Pursuit. 157 | 158 | ## [v0.2.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v0.2.0) - 2015-11-11 159 | 160 | - Major important fixes. This is a breaking change as type signatures have changed. (@hdgarrood) 161 | 162 | ## [v0.1.1](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v0.1.1) - 2015-11-10 163 | 164 | - Fixed `write`, `writeString`, and `fill`, which used to immediately throw errors at runtime upon use. 165 | 166 | ## [v0.1.0](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v0.1.0) - 2015-07-06 167 | 168 | - Support for compiler version 0.7 169 | - Use Int instead of Number where appropriate (which happens to be everywhere) 170 | 171 | ## [v0.0.1](https://github.com/purescript-node/purescript-node-buffer/releases/tag/v0.0.1) - 2014-10-14 172 | 173 | - Initial versioned release 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 PureScript 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # purescript-node-buffer 2 | 3 | [![Latest release](http://img.shields.io/github/release/purescript-node/purescript-node-buffer.svg)](https://github.com/purescript-node/purescript-node-buffer/releases) 4 | [![Build status](https://github.com/purescript-node/purescript-node-buffer/workflows/CI/badge.svg?branch=master)](https://github.com/purescript-node/purescript-node-buffer/actions?query=workflow%3ACI+branch%3Amaster) 5 | [![Pursuit](https://pursuit.purescript.org/packages/purescript-node-buffer/badge)](https://pursuit.purescript.org/packages/purescript-node-buffer) 6 | 7 | Type declarations and FFI wrappers for Node's [Buffer API](https://nodejs.org/api/buffer.html). 8 | 9 | ## Installation 10 | 11 | ``` 12 | spago install node-buffer 13 | ``` 14 | 15 | ## Documentation 16 | 17 | Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-node-buffer). 18 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "purescript-node-buffer", 3 | "license": "MIT", 4 | "ignore": [ 5 | "**/.*", 6 | "node_modules", 7 | "bower_components", 8 | "test", 9 | "tests" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/purescript-node/purescript-node-buffer.git" 14 | }, 15 | "dependencies": { 16 | "purescript-arraybuffer-types": "^3.0.2", 17 | "purescript-effect": "^4.0.0", 18 | "purescript-maybe": "^6.0.0", 19 | "purescript-st": "^6.0.0", 20 | "purescript-unsafe-coerce": "^6.0.0", 21 | "purescript-nullable": "^6.0.0" 22 | }, 23 | "devDependencies": { 24 | "purescript-assert": "^6.0.0", 25 | "purescript-console": "^6.0.0", 26 | "purescript-foldable-traversable": "^6.0.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "purescript-node-buffer", 3 | "private": true, 4 | "scripts": { 5 | "clean": "rimraf output && rimraf .pulp-cache", 6 | "build": "eslint src && pulp build -- --censor-lib --strict", 7 | "test": "pulp test -- --censor-lib --strict" 8 | }, 9 | "devDependencies": { 10 | "eslint": "^7.15.0", 11 | "pulp": "16.0.0-0", 12 | "purescript-psa": "^0.8.2", 13 | "rimraf": "^3.0.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Node/Buffer.js: -------------------------------------------------------------------------------- 1 | import { Buffer, transcode } from "node:buffer"; 2 | 3 | export const allocUnsafeImpl = (size) => Buffer.allocUnsafe(size); 4 | export const allocUnsafeSlowImpl = (size) => Buffer.allocUnsafeSlow(size); 5 | 6 | export const freezeImpl = (a) => Buffer.from(a); 7 | export const thawImpl = (a) => Buffer.from(a); 8 | 9 | export const writeInternal = (ty, value, offset, buf) => buf["write" + ty](value, offset); 10 | 11 | export const writeStringInternal = (encoding, offset, length, value, buff) => 12 | buff.write(value, offset, length, encoding); 13 | 14 | export const setAtOffsetImpl = (value, offset, buff) => { 15 | buff[offset] = value; 16 | }; 17 | 18 | export const copyImpl = (srcStart, srcEnd, src, targStart, targ) => 19 | src.copy(targ, targStart, srcStart, srcEnd); 20 | 21 | export const fillImpl = (octet, start, end, buf) => 22 | buf.fill(octet, start, end); 23 | 24 | export const poolSize = () => Buffer.poolSize; 25 | 26 | export const setPoolSizeImpl = (size) => { 27 | Buffer.poolSize = size; 28 | }; 29 | 30 | export const swap16Impl = (buf) => buf.swap16(); 31 | export const swap32Impl = (buf) => buf.swap32(); 32 | export const swap64Impl = (buf) => buf.swap64(); 33 | export const transcodeImpl = (buf, from, to) => transcode(buf, from, to); 34 | -------------------------------------------------------------------------------- /src/Node/Buffer.purs: -------------------------------------------------------------------------------- 1 | -- | Mutable buffers and associated operations. 2 | module Node.Buffer 3 | ( Buffer 4 | , module TypesExports 5 | , create 6 | , alloc 7 | , allocUnsafe 8 | , allocUnsafeSlow 9 | , compareParts 10 | , freeze 11 | , unsafeFreeze 12 | , thaw 13 | , unsafeThaw 14 | , fromArray 15 | , fromString 16 | , fromArrayBuffer 17 | , toArrayBuffer 18 | , read 19 | , readString 20 | , toString 21 | , toString' 22 | , write 23 | , writeString 24 | , toArray 25 | , getAtOffset 26 | , setAtOffset 27 | , slice 28 | , size 29 | , concat 30 | , concat' 31 | , copy 32 | , fill 33 | , poolSize 34 | , setPoolSize 35 | , swap16 36 | , swap32 37 | , swap64 38 | , transcode 39 | ) where 40 | 41 | import Prelude 42 | 43 | import Data.ArrayBuffer.Types (ArrayBuffer) 44 | import Data.Maybe (Maybe) 45 | import Effect (Effect) 46 | import Effect.Uncurried (EffectFn1, EffectFn3, EffectFn4, EffectFn5, runEffectFn1, runEffectFn3, runEffectFn4, runEffectFn5) 47 | import Node.Buffer.Class (class MutableBuffer) 48 | import Node.Buffer.Immutable (ImmutableBuffer) 49 | import Node.Buffer.Immutable as Immutable 50 | import Node.Buffer.Types (BufferValueType(..), Octet, Offset) as TypesExports 51 | import Node.Buffer.Types (BufferValueType, Octet, Offset) 52 | import Node.Encoding (Encoding, encodingToNode) 53 | import Unsafe.Coerce (unsafeCoerce) 54 | 55 | -- | A reference to a mutable buffer for use with `Effect` 56 | foreign import data Buffer :: Type 57 | 58 | instance mutableBufferEffect :: MutableBuffer Buffer Effect where 59 | create = create 60 | freeze = freeze 61 | unsafeFreeze = unsafeFreeze 62 | thaw = thaw 63 | unsafeThaw = unsafeThaw 64 | fromArray = fromArray 65 | fromString = fromString 66 | fromArrayBuffer = fromArrayBuffer 67 | toArrayBuffer = toArrayBuffer 68 | read = read 69 | readString = readString 70 | toString = toString 71 | write = write 72 | writeString = writeString 73 | toArray = toArray 74 | getAtOffset = getAtOffset 75 | setAtOffset = setAtOffset 76 | slice = slice 77 | size = size 78 | concat = concat 79 | concat' = concat' 80 | copy = copy 81 | fill = fill 82 | 83 | unsafeFreeze :: Buffer -> Effect ImmutableBuffer 84 | unsafeFreeze = pure <<< unsafeCoerce 85 | 86 | unsafeThaw :: ImmutableBuffer -> Effect Buffer 87 | unsafeThaw = pure <<< unsafeCoerce 88 | 89 | usingFromImmutable :: forall a. (ImmutableBuffer -> a) -> Buffer -> Effect a 90 | usingFromImmutable f buf = f <$> unsafeFreeze buf 91 | 92 | usingToImmutable :: forall a. (a -> ImmutableBuffer) -> a -> Effect Buffer 93 | usingToImmutable f x = unsafeThaw $ f x 94 | 95 | -- | Creates a new buffer of the specified size. Alias to `alloc`. 96 | create :: Int -> Effect Buffer 97 | create = alloc 98 | 99 | -- | Creates a new buffer of the specified size. 100 | alloc :: Int -> Effect Buffer 101 | alloc = usingToImmutable Immutable.alloc 102 | 103 | -- | Creates a new buffer of the specified size. Unsafe because it reuses memory from a pool 104 | -- | and may contain sensitive data. See the Node docs: 105 | -- | https://nodejs.org/docs/latest-v18.x/api/buffer.html#what-makes-bufferallocunsafe-and-bufferallocunsafeslow-unsafe 106 | allocUnsafe :: Int -> Effect Buffer 107 | allocUnsafe s = runEffectFn1 allocUnsafeImpl s 108 | 109 | foreign import allocUnsafeImpl :: EffectFn1 (Int) (Buffer) 110 | 111 | -- | Creates a new buffer of the specified size. Unsafe because it reuses memory from a pool 112 | -- | and may contain sensitive data. See the Node docs: 113 | -- | https://nodejs.org/docs/latest-v18.x/api/buffer.html#what-makes-bufferallocunsafe-and-bufferallocunsafeslow-unsafe 114 | allocUnsafeSlow :: Int -> Effect Buffer 115 | allocUnsafeSlow s = runEffectFn1 allocUnsafeSlowImpl s 116 | 117 | foreign import allocUnsafeSlowImpl :: EffectFn1 (Int) (Buffer) 118 | 119 | freeze :: Buffer -> Effect ImmutableBuffer 120 | freeze = runEffectFn1 freezeImpl 121 | 122 | foreign import freezeImpl :: EffectFn1 Buffer ImmutableBuffer 123 | 124 | thaw :: ImmutableBuffer -> Effect Buffer 125 | thaw = runEffectFn1 thawImpl 126 | 127 | foreign import thawImpl :: EffectFn1 ImmutableBuffer Buffer 128 | 129 | fromArray :: Array Octet -> Effect Buffer 130 | fromArray = usingToImmutable Immutable.fromArray 131 | 132 | fromString :: String -> Encoding -> Effect Buffer 133 | fromString s = usingToImmutable $ Immutable.fromString s 134 | 135 | fromArrayBuffer :: ArrayBuffer -> Effect Buffer 136 | fromArrayBuffer = usingToImmutable Immutable.fromArrayBuffer 137 | 138 | toArrayBuffer :: Buffer -> Effect ArrayBuffer 139 | toArrayBuffer = usingFromImmutable Immutable.toArrayBuffer 140 | 141 | compareParts :: Buffer -> Buffer -> Offset -> Offset -> Offset -> Offset -> Effect Ordering 142 | compareParts src target targetSrc targetEnd srcStart srcEnd = do 143 | src' <- unsafeFreeze src 144 | target' <- unsafeFreeze target 145 | Immutable.compareParts src' target' targetSrc targetEnd srcStart srcEnd 146 | 147 | read :: BufferValueType -> Offset -> Buffer -> Effect Number 148 | read t o = usingFromImmutable $ Immutable.read t o 149 | 150 | readString :: Encoding -> Offset -> Offset -> Buffer -> Effect String 151 | readString enc o o' = usingFromImmutable $ Immutable.readString enc o o' 152 | 153 | toString :: Encoding -> Buffer -> Effect String 154 | toString enc = usingFromImmutable $ Immutable.toString enc 155 | 156 | toString' :: Encoding -> Offset -> Offset -> Buffer -> Effect String 157 | toString' enc start end = usingFromImmutable $ Immutable.toString' enc start end 158 | 159 | write :: BufferValueType -> Number -> Offset -> Buffer -> Effect Unit 160 | write ty value offset buf = runEffectFn4 writeInternal (show ty) value offset buf 161 | 162 | foreign import writeInternal :: EffectFn4 String Number Offset Buffer Unit 163 | 164 | writeString :: Encoding -> Offset -> Int -> String -> Buffer -> Effect Int 165 | writeString enc offset len value buf = 166 | runEffectFn5 writeStringInternal (encodingToNode enc) offset len value buf 167 | 168 | foreign import writeStringInternal :: EffectFn5 String Offset Int String Buffer Int 169 | 170 | toArray :: Buffer -> Effect (Array Octet) 171 | toArray = usingFromImmutable Immutable.toArray 172 | 173 | getAtOffset :: Offset -> Buffer -> Effect (Maybe Octet) 174 | getAtOffset o = usingFromImmutable $ Immutable.getAtOffset o 175 | 176 | setAtOffset :: Octet -> Offset -> Buffer -> Effect Unit 177 | setAtOffset val off buff = runEffectFn3 setAtOffsetImpl val off buff 178 | 179 | foreign import setAtOffsetImpl :: EffectFn3 Octet Offset Buffer Unit 180 | 181 | slice :: Offset -> Offset -> Buffer -> Buffer 182 | slice = unsafeCoerce Immutable.slice 183 | 184 | size :: Buffer -> Effect Int 185 | size = usingFromImmutable Immutable.size 186 | 187 | concat :: Array Buffer -> Effect Buffer 188 | concat arrs = unsafeCoerce \_ -> Immutable.concat (unsafeCoerce arrs) 189 | 190 | concat' :: Array Buffer -> Int -> Effect Buffer 191 | concat' arrs n = unsafeCoerce \_ -> Immutable.concat' (unsafeCoerce arrs) n 192 | 193 | copy :: Offset -> Offset -> Buffer -> Offset -> Buffer -> Effect Int 194 | copy srcStart srcEnd src targStart targ = do 195 | runEffectFn5 copyImpl srcStart srcEnd src targStart targ 196 | 197 | foreign import copyImpl :: EffectFn5 Offset Offset Buffer Offset Buffer Int 198 | 199 | fill :: Octet -> Offset -> Offset -> Buffer -> Effect Unit 200 | fill octet start end buf = do 201 | runEffectFn4 fillImpl octet start end buf 202 | 203 | foreign import fillImpl :: EffectFn4 Octet Offset Offset Buffer Unit 204 | 205 | -- | The size (in bytes) of pre-allocated internal Buffer instances used for pooling. This value may be modified. 206 | foreign import poolSize :: Effect (Int) 207 | 208 | setPoolSize :: Int -> Effect Unit 209 | setPoolSize sizeInBytes = runEffectFn1 setPoolSizeImpl sizeInBytes 210 | 211 | foreign import setPoolSizeImpl :: EffectFn1 (Int) (Unit) 212 | 213 | swap16 :: Buffer -> Effect Buffer 214 | swap16 b = runEffectFn1 swap16Impl b 215 | 216 | foreign import swap16Impl :: EffectFn1 (Buffer) (Buffer) 217 | 218 | swap32 :: Buffer -> Effect Buffer 219 | swap32 b = runEffectFn1 swap32Impl b 220 | 221 | foreign import swap32Impl :: EffectFn1 (Buffer) (Buffer) 222 | 223 | swap64 :: Buffer -> Effect Buffer 224 | swap64 b = runEffectFn1 swap64Impl b 225 | 226 | foreign import swap64Impl :: EffectFn1 (Buffer) (Buffer) 227 | 228 | transcode :: Buffer -> Encoding -> Encoding -> Effect Buffer 229 | transcode buf from to = runEffectFn3 transcodeImpl buf (encodingToNode from) (encodingToNode to) 230 | 231 | foreign import transcodeImpl :: EffectFn3 (Buffer) (String) (String) (Buffer) 232 | -------------------------------------------------------------------------------- /src/Node/Buffer/Class.purs: -------------------------------------------------------------------------------- 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 Number 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 -> Number -> 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 116 | -------------------------------------------------------------------------------- /src/Node/Buffer/Constants.js: -------------------------------------------------------------------------------- 1 | import buffer from "node:buffer"; 2 | 3 | export const inspectMaxBytes = () => buffer.INSPECT_MAX_LENGTH; 4 | export const maxLength = buffer.constants.MAX_LENGTH; 5 | export const maxStringLength = buffer.constants.MAX_STRING_LENGTH; 6 | -------------------------------------------------------------------------------- /src/Node/Buffer/Constants.purs: -------------------------------------------------------------------------------- 1 | module Node.Buffer.Constants where 2 | 3 | import Effect (Effect) 4 | 5 | foreign import inspectMaxBytes :: Effect Int 6 | 7 | foreign import maxLength :: Int 8 | 9 | foreign import maxStringLength :: Int 10 | -------------------------------------------------------------------------------- /src/Node/Buffer/Immutable.js: -------------------------------------------------------------------------------- 1 | import { Buffer } from "node:buffer"; 2 | 3 | import { inspect } from "util"; 4 | export const showImpl = inspect; 5 | 6 | export const eqImpl = (a, b) => a.equals(b); 7 | 8 | export const compareImpl = (a, b) => a.compare(b); 9 | 10 | export const comparePartsImpl = (src, target, targetStart, targetEnd, sourceStart, sourceEnd) => 11 | src.compare(target, targetStart, targetEnd, sourceStart, sourceEnd); 12 | 13 | export const alloc = (size) => Buffer.alloc(size); 14 | 15 | export const fromArray = (octets) => Buffer.from(octets); 16 | 17 | export const size = (buff) => buff.length; 18 | 19 | export function toArray(buff) { 20 | var json = buff.toJSON(); 21 | return json.data || json; 22 | } 23 | 24 | export const toArrayBuffer = (buff) => 25 | buff.buffer.slice(buff.byteOffset, buff.byteOffset + buff.byteLength); 26 | 27 | export const fromArrayBuffer = (ab) => Buffer.from(ab); 28 | 29 | export const fromStringImpl = (str, encoding) => Buffer.from(str, encoding); 30 | 31 | export const readImpl = (ty, offset, buf) => buf["read" + ty](offset); 32 | 33 | export const readStringImpl = (enc, start, end, buff) => buff.toString(enc, start, end); 34 | 35 | export const getAtOffsetImpl = (offset, buff) => buff[offset]; 36 | 37 | export const toStringImpl = (enc, buff) => buff.toString(enc); 38 | 39 | export const toStringSubImpl = (enc, start, end, buff) => buff.toString(enc, start, end); 40 | 41 | export const sliceImpl = (start, end, buff) => buff.slice(start, end); 42 | 43 | export const concat = (buffs) => Buffer.concat(buffs); 44 | 45 | export const concatToLength = (buffs, totalLength) => Buffer.concat(buffs, totalLength); 46 | -------------------------------------------------------------------------------- /src/Node/Buffer/Immutable.purs: -------------------------------------------------------------------------------- 1 | -- | Immutable buffers and associated operations. 2 | module Node.Buffer.Immutable 3 | ( ImmutableBuffer 4 | , compareParts 5 | , create 6 | , alloc 7 | , fromArray 8 | , fromString 9 | , fromArrayBuffer 10 | , read 11 | , readString 12 | , toString 13 | , toString' 14 | , toArray 15 | , toArrayBuffer 16 | , getAtOffset 17 | , concat 18 | , concat' 19 | , slice 20 | , size 21 | ) where 22 | 23 | import Prelude 24 | 25 | import Data.ArrayBuffer.Types (ArrayBuffer) 26 | import Data.Function.Uncurried (Fn2, Fn3, Fn4, runFn2, runFn3, runFn4) 27 | import Data.Maybe (Maybe) 28 | import Data.Nullable (Nullable, toMaybe) 29 | import Effect (Effect) 30 | import Effect.Uncurried (EffectFn6, runEffectFn6) 31 | import Node.Buffer.Types (BufferValueType, Octet, Offset) 32 | import Node.Encoding (Encoding, encodingToNode) 33 | import Partial.Unsafe (unsafeCrashWith) 34 | 35 | -- | An immutable buffer that exists independently of any memory region or effect. 36 | foreign import data ImmutableBuffer :: Type 37 | 38 | instance showBuffer :: Show ImmutableBuffer where 39 | show = showImpl 40 | 41 | foreign import showImpl :: ImmutableBuffer -> String 42 | 43 | instance eqBuffer :: Eq ImmutableBuffer where 44 | eq a b = runFn2 eqImpl a b 45 | 46 | foreign import eqImpl :: Fn2 ImmutableBuffer ImmutableBuffer Boolean 47 | 48 | instance ordBuffer :: Ord ImmutableBuffer where 49 | compare a b = 50 | case runFn2 compareImpl a b of 51 | x | x < 0 -> LT 52 | x | x > 0 -> GT 53 | _ -> EQ 54 | 55 | foreign import compareImpl :: Fn2 ImmutableBuffer ImmutableBuffer Int 56 | 57 | -- | Creates a new buffer of the specified size. Alias for `alloc`. 58 | create :: Int -> ImmutableBuffer 59 | create = alloc 60 | 61 | -- | Creates a new buffer of the specified size. 62 | foreign import alloc :: Int -> ImmutableBuffer 63 | 64 | -- | Creates a new buffer from an array of octets, sized to match the array. 65 | foreign import fromArray :: Array Octet -> ImmutableBuffer 66 | 67 | -- | Creates a buffer view from a JS ArrayByffer without copying data. 68 | foreign import fromArrayBuffer :: ArrayBuffer -> ImmutableBuffer 69 | 70 | -- | Creates a new buffer from a string with the specified encoding, sized to match the string. 71 | fromString :: String -> Encoding -> ImmutableBuffer 72 | fromString str enc = runFn2 fromStringImpl str $ encodingToNode enc 73 | 74 | foreign import fromStringImpl :: Fn2 String String ImmutableBuffer 75 | 76 | compareParts :: ImmutableBuffer -> ImmutableBuffer -> Offset -> Offset -> Offset -> Offset -> Effect Ordering 77 | compareParts src target targetStart targetEnd sourceStart sourceEnd = 78 | runEffectFn6 comparePartsImpl src target targetStart targetEnd sourceStart sourceEnd <#> case _ of 79 | -1 -> LT 80 | 0 -> EQ 81 | 1 -> GT 82 | x -> unsafeCrashWith $ "Impossible: Invalid value: " <> show x 83 | 84 | foreign import comparePartsImpl :: EffectFn6 ImmutableBuffer ImmutableBuffer Int Int Int Int Int 85 | 86 | -- | Reads a numeric value from a buffer at the specified offset. 87 | read :: BufferValueType -> Offset -> ImmutableBuffer -> Number 88 | read ty off buf = runFn3 readImpl (show ty) off buf 89 | 90 | foreign import readImpl :: Fn3 String Offset ImmutableBuffer Number 91 | 92 | -- | Reads a section of a buffer as a string with the specified encoding. 93 | readString :: Encoding -> Offset -> Offset -> ImmutableBuffer -> String 94 | readString enc start end buf = runFn4 readStringImpl (encodingToNode enc) start end buf 95 | 96 | foreign import readStringImpl :: Fn4 String Offset Offset ImmutableBuffer String 97 | 98 | -- | Reads the buffer as a string with the specified encoding. 99 | toString :: Encoding -> ImmutableBuffer -> String 100 | toString enc buf = runFn2 toStringImpl (encodingToNode enc) buf 101 | 102 | foreign import toStringImpl :: Fn2 String ImmutableBuffer String 103 | 104 | toString' :: Encoding -> Offset -> Offset -> ImmutableBuffer -> String 105 | toString' enc start end buf = runFn4 toStringSubImpl enc start end buf 106 | 107 | foreign import toStringSubImpl :: Fn4 Encoding Offset Offset ImmutableBuffer String 108 | 109 | -- | Creates an array of octets from a buffer's contents. 110 | foreign import toArray :: ImmutableBuffer -> Array Octet 111 | 112 | -- | Creates an `ArrayBuffer` by copying a buffer's contents. 113 | foreign import toArrayBuffer :: ImmutableBuffer -> ArrayBuffer 114 | 115 | -- | Reads an octet from a buffer at the specified offset. 116 | getAtOffset :: Offset -> ImmutableBuffer -> Maybe Octet 117 | getAtOffset off buf = toMaybe $ runFn2 getAtOffsetImpl off buf 118 | 119 | foreign import getAtOffsetImpl :: Fn2 Offset ImmutableBuffer (Nullable Octet) 120 | 121 | -- | Concatenates a list of buffers. 122 | foreign import concat :: Array ImmutableBuffer -> ImmutableBuffer 123 | 124 | -- | Concatenates a list of buffers, combining them into a new buffer of the 125 | -- | specified length. 126 | concat' :: Array ImmutableBuffer -> Int -> ImmutableBuffer 127 | concat' a i = runFn2 concatToLength a i 128 | 129 | foreign import concatToLength :: Fn2 (Array ImmutableBuffer) Int ImmutableBuffer 130 | 131 | -- | Creates a new buffer slice that shares the memory of the original buffer. 132 | slice :: Offset -> Offset -> ImmutableBuffer -> ImmutableBuffer 133 | slice start end buf = runFn3 sliceImpl start end buf 134 | 135 | foreign import sliceImpl :: Fn3 Offset Offset ImmutableBuffer ImmutableBuffer 136 | 137 | -- | Returns the size of a buffer. 138 | foreign import size :: ImmutableBuffer -> Int 139 | -------------------------------------------------------------------------------- /src/Node/Buffer/ST.purs: -------------------------------------------------------------------------------- 1 | module Node.Buffer.ST 2 | ( STBuffer 3 | , run 4 | , create 5 | , alloc 6 | , allocUnsafe 7 | , allocUnsafeSlow 8 | , compareParts 9 | , freeze 10 | , unsafeFreeze 11 | , thaw 12 | , unsafeThaw 13 | , fromArray 14 | , fromString 15 | , fromArrayBuffer 16 | , toArrayBuffer 17 | , read 18 | , readString 19 | , toString 20 | , toString' 21 | , write 22 | , writeString 23 | , toArray 24 | , getAtOffset 25 | , setAtOffset 26 | , slice 27 | , size 28 | , concat 29 | , concat' 30 | , copy 31 | , fill 32 | , swap16 33 | , swap32 34 | , swap64 35 | , transcode 36 | ) where 37 | 38 | import Prelude 39 | 40 | import Control.Monad.ST (ST, Region) 41 | import Control.Monad.ST as ST 42 | import Data.ArrayBuffer.Types (ArrayBuffer) 43 | import Data.Maybe (Maybe) 44 | import Node.Buffer (BufferValueType, Octet, Offset) 45 | import Node.Buffer as Buffer 46 | import Node.Buffer.Class (class MutableBuffer) 47 | import Node.Buffer.Immutable (ImmutableBuffer) 48 | import Node.Encoding (Encoding) 49 | import Unsafe.Coerce (unsafeCoerce) 50 | 51 | -- | A reference to a mutable Buffer for use with `ST` 52 | -- | 53 | -- | The type parameter represents the memory region which the (STBuffer h) belongs to. 54 | foreign import data STBuffer :: Region -> Type 55 | 56 | -- | Runs an ST creating an `STBuffer` then freezes the (STBuffer h) and returns 57 | -- | it, without unneccessary copying. 58 | run :: (forall h. ST h (STBuffer h)) -> ImmutableBuffer 59 | run st = ST.run (st >>= unsafeFreeze) 60 | 61 | instance mutableBufferST :: MutableBuffer (STBuffer h) (ST h) where 62 | create = create 63 | freeze = freeze 64 | unsafeFreeze = unsafeFreeze 65 | thaw = thaw 66 | unsafeThaw = unsafeThaw 67 | fromArray = fromArray 68 | fromString = fromString 69 | fromArrayBuffer = fromArrayBuffer 70 | toArrayBuffer = toArrayBuffer 71 | read = read 72 | readString = readString 73 | toString = toString 74 | write = write 75 | writeString = writeString 76 | toArray = toArray 77 | getAtOffset = getAtOffset 78 | setAtOffset = setAtOffset 79 | slice = slice 80 | size = size 81 | concat = concat 82 | concat' = concat' 83 | copy = copy 84 | fill = fill 85 | 86 | -- | Creates a new `STBuffer`. Alias of `alloc`. 87 | create :: forall h. Int -> ST h (STBuffer h) 88 | create = alloc 89 | 90 | -- | Creates a new `STBuffer`. 91 | alloc :: forall h. Int -> ST h (STBuffer h) 92 | alloc = unsafeCoerce Buffer.alloc 93 | 94 | -- | Creates a new `STBuffer` of the specified size. Unsafe because it reuses memory from a pool 95 | -- | and may contain sensitive data. See the Node docs: 96 | -- | https://nodejs.org/docs/latest-v18.x/api/buffer.html#what-makes-bufferallocunsafe-and-bufferallocunsafeslow-unsafe 97 | allocUnsafe :: forall h. Int -> ST h (STBuffer h) 98 | allocUnsafe = unsafeCoerce Buffer.allocUnsafe 99 | 100 | -- | Creates a new `STBuffer` of the specified size. Unsafe because it reuses memory from a pool 101 | -- | and may contain sensitive data. See the Node docs: 102 | -- | https://nodejs.org/docs/latest-v18.x/api/buffer.html#what-makes-bufferallocunsafe-and-bufferallocunsafeslow-unsafe 103 | allocUnsafeSlow :: forall h. Int -> ST h (STBuffer h) 104 | allocUnsafeSlow = unsafeCoerce Buffer.allocUnsafeSlow 105 | 106 | compareParts :: forall h. STBuffer h -> STBuffer h -> Int -> Int -> Int -> Int -> ST h Ordering 107 | compareParts = unsafeCoerce Buffer.compareParts 108 | 109 | freeze :: forall h. (STBuffer h) -> ST h ImmutableBuffer 110 | freeze = unsafeCoerce Buffer.freeze 111 | 112 | unsafeFreeze :: forall h. STBuffer h -> ST h ImmutableBuffer 113 | unsafeFreeze = unsafeCoerce Buffer.unsafeFreeze 114 | 115 | thaw :: forall h. ImmutableBuffer -> ST h (STBuffer h) 116 | thaw = unsafeCoerce Buffer.thaw 117 | 118 | unsafeThaw :: forall h. ImmutableBuffer -> ST h (STBuffer h) 119 | unsafeThaw = unsafeCoerce Buffer.unsafeThaw 120 | 121 | fromArray :: forall h. Array Octet -> ST h (STBuffer h) 122 | fromArray = unsafeCoerce Buffer.fromArray 123 | 124 | fromString :: forall h. String -> Encoding -> ST h (STBuffer h) 125 | fromString = unsafeCoerce Buffer.fromString 126 | 127 | fromArrayBuffer :: forall h. ArrayBuffer -> ST h (STBuffer h) 128 | fromArrayBuffer = unsafeCoerce Buffer.fromArrayBuffer 129 | 130 | toArrayBuffer :: forall h. STBuffer h -> ST h ArrayBuffer 131 | toArrayBuffer = unsafeCoerce Buffer.toArrayBuffer 132 | 133 | read :: forall h. BufferValueType -> Offset -> STBuffer h -> ST h Number 134 | read = unsafeCoerce Buffer.read 135 | 136 | readString :: forall h. Encoding -> Offset -> Offset -> STBuffer h -> ST h String 137 | readString = unsafeCoerce Buffer.readString 138 | 139 | toString :: forall h. Encoding -> STBuffer h -> ST h String 140 | toString = unsafeCoerce Buffer.toString 141 | 142 | toString' :: forall h. Encoding -> Offset -> Offset -> STBuffer h -> ST h String 143 | toString' = unsafeCoerce Buffer.toString' 144 | 145 | write :: forall h. BufferValueType -> Number -> Offset -> STBuffer h -> ST h Unit 146 | write = unsafeCoerce Buffer.write 147 | 148 | writeString :: forall h. Encoding -> Offset -> Int -> String -> STBuffer h -> ST h Int 149 | writeString = 150 | unsafeCoerce Buffer.writeString 151 | 152 | toArray :: forall h. STBuffer h -> ST h (Array Octet) 153 | toArray = unsafeCoerce Buffer.toArray 154 | 155 | getAtOffset :: forall h. Offset -> STBuffer h -> ST h (Maybe Octet) 156 | getAtOffset = unsafeCoerce Buffer.getAtOffset 157 | 158 | setAtOffset :: forall h. Octet -> Offset -> STBuffer h -> ST h Unit 159 | setAtOffset = unsafeCoerce Buffer.setAtOffset 160 | 161 | slice :: forall h. Offset -> Offset -> STBuffer h -> STBuffer h 162 | slice = unsafeCoerce Buffer.slice 163 | 164 | size :: forall h. STBuffer h -> ST h Int 165 | size = unsafeCoerce Buffer.size 166 | 167 | concat :: forall h. Array (STBuffer h) -> ST h (STBuffer h) 168 | concat = unsafeCoerce Buffer.concat 169 | 170 | concat' :: forall h. Array (STBuffer h) -> Int -> ST h (STBuffer h) 171 | concat' = unsafeCoerce Buffer.concat' 172 | 173 | copy :: forall h. Offset -> Offset -> STBuffer h -> Offset -> STBuffer h -> ST h Int 174 | copy = unsafeCoerce Buffer.copy 175 | 176 | fill :: forall h. Octet -> Offset -> Offset -> STBuffer h -> ST h Unit 177 | fill = unsafeCoerce Buffer.fill 178 | 179 | swap16 :: forall h. STBuffer h -> ST h (STBuffer h) 180 | swap16 = unsafeCoerce Buffer.swap16 181 | 182 | swap32 :: forall h. STBuffer h -> ST h (STBuffer h) 183 | swap32 = unsafeCoerce Buffer.swap32 184 | 185 | swap64 :: forall h. STBuffer h -> ST h (STBuffer h) 186 | swap64 = unsafeCoerce Buffer.swap64 187 | 188 | transcode :: forall h. STBuffer h -> Encoding -> Encoding -> ST h (STBuffer h) 189 | transcode = unsafeCoerce Buffer.transcode 190 | -------------------------------------------------------------------------------- /src/Node/Buffer/Types.purs: -------------------------------------------------------------------------------- 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" 48 | -------------------------------------------------------------------------------- /src/Node/Encoding.js: -------------------------------------------------------------------------------- 1 | import { Buffer } from "node:buffer"; 2 | 3 | export const byteLengthImpl = (str, enc) => Buffer.byteLength(str, enc); 4 | -------------------------------------------------------------------------------- /src/Node/Encoding.purs: -------------------------------------------------------------------------------- 1 | module Node.Encoding 2 | ( Encoding(..) 3 | , encodingToNode 4 | , byteLength 5 | ) where 6 | 7 | import Prelude 8 | 9 | import Data.Function.Uncurried (Fn2, runFn2) 10 | 11 | data Encoding 12 | = ASCII 13 | | UTF8 14 | | UTF16LE 15 | | UCS2 16 | | Base64 17 | | Base64Url 18 | | Latin1 19 | | Binary 20 | | Hex 21 | 22 | instance showEncoding :: Show Encoding where 23 | show ASCII = "ASCII" 24 | show UTF8 = "UTF8" 25 | show UTF16LE = "UTF16LE" 26 | show UCS2 = "UCS2" 27 | show Base64 = "Base64" 28 | show Base64Url = "Base64Url" 29 | show Latin1 = "Latin1" 30 | show Binary = "Binary" 31 | show Hex = "Hex" 32 | 33 | -- | Convert an `Encoding` to a `String` in the format expected by Node.js 34 | -- | APIs. 35 | encodingToNode :: Encoding -> String 36 | encodingToNode ASCII = "ascii" 37 | encodingToNode UTF8 = "utf8" 38 | encodingToNode UTF16LE = "utf16le" 39 | encodingToNode UCS2 = "ucs2" 40 | encodingToNode Base64 = "base64" 41 | encodingToNode Base64Url = "base64url" 42 | encodingToNode Latin1 = "latin1" 43 | encodingToNode Binary = "binary" 44 | encodingToNode Hex = "hex" 45 | 46 | foreign import byteLengthImpl :: Fn2 String String Int 47 | 48 | byteLength :: String -> Encoding -> Int 49 | byteLength str enc = runFn2 byteLengthImpl str (encodingToNode enc) 50 | -------------------------------------------------------------------------------- /test/Test/Main.purs: -------------------------------------------------------------------------------- 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 9 | -------------------------------------------------------------------------------- /test/Test/Node/Buffer.purs: -------------------------------------------------------------------------------- 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 21 | -------------------------------------------------------------------------------- /test/Test/Node/Buffer/Class.purs: -------------------------------------------------------------------------------- 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 (class MutableBuffer, concat', copy, create, fill, freeze, fromArray, fromArrayBuffer, fromString, getAtOffset, read, readString, setAtOffset, slice, thaw, toArray, toArrayBuffer, toString, write) 11 | import Node.Buffer (BufferValueType(..)) 12 | import Node.Buffer.Immutable as Immutable 13 | import Node.Encoding (Encoding(..)) 14 | import Test.Assert (assertEqual) 15 | import Type.Proxy (Proxy) 16 | 17 | testMutableBuffer 18 | :: forall buf m 19 | . MutableBuffer buf m 20 | => Proxy buf 21 | -> (forall a. m a -> Effect a) 22 | -> Effect Unit 23 | testMutableBuffer _ run = do 24 | 25 | log " - create" 26 | testCreate 27 | 28 | log " - freeze" 29 | testFreeze 30 | 31 | log " - thaw" 32 | testThaw 33 | 34 | log " - Reading and writing" 35 | testReadWrite 36 | 37 | log " - fromArray" 38 | testFromArray 39 | 40 | log " - toArray" 41 | testToArray 42 | 43 | log " - fromString" 44 | testFromString 45 | 46 | log " - (to/from)ArrayBuffer" 47 | testToFromArrayBuffer 48 | 49 | log " - toString" 50 | testToString 51 | 52 | log " - readString" 53 | testReadString 54 | 55 | log " - slice" 56 | testSlice 57 | 58 | log " - copy" 59 | testCopy 60 | 61 | log " - fill" 62 | testFill 63 | 64 | log " - concat'" 65 | testConcat' 66 | 67 | log " - getAtOffset" 68 | testGetAtOffset 69 | 70 | where 71 | testCreate :: Effect Unit 72 | testCreate = do 73 | buf <- run ((create 3 :: m buf) >>= toArray) 74 | assertEqual { expected: [ 0, 0, 0 ], actual: buf } 75 | 76 | testFreeze :: Effect Unit 77 | testFreeze = do 78 | buf <- Immutable.toArray <$> run ((fromArray [ 1, 2, 3 ] :: m buf) >>= freeze) 79 | assertEqual { expected: [ 1, 2, 3 ], actual: buf } 80 | 81 | testThaw :: Effect Unit 82 | testThaw = do 83 | buf <- run ((thaw (Immutable.fromArray [ 1, 2, 3 ]) :: m buf) >>= toArray) 84 | assertEqual { expected: [ 1, 2, 3 ], actual: buf } 85 | 86 | testReadWrite :: Effect Unit 87 | testReadWrite = do 88 | let val = 42.0 89 | readVal <- run do 90 | buf <- create 1 :: m buf 91 | write UInt8 val 0 buf 92 | read UInt8 0 buf 93 | 94 | assertEqual { expected: val, actual: readVal } 95 | 96 | testFromArray :: Effect Unit 97 | testFromArray = do 98 | readVal <- run do 99 | buf <- fromArray [ 1, 2, 3, 4, 5 ] :: m buf 100 | read UInt8 2 buf 101 | 102 | assertEqual { expected: 3.0, actual: readVal } 103 | 104 | testToArray :: Effect Unit 105 | testToArray = do 106 | let val = [ 1, 2, 67, 3, 3, 7, 8, 3, 4, 237 ] 107 | valOut <- run do 108 | buf <- fromArray val :: m buf 109 | toArray buf 110 | 111 | assertEqual { expected: val, actual: valOut } 112 | 113 | testFromString :: Effect Unit 114 | testFromString = do 115 | let str = "hello, world" 116 | val <- run do 117 | buf <- fromString str ASCII :: m buf 118 | read UInt8 6 buf 119 | 120 | assertEqual { expected: 32.0, actual: val } -- ASCII space 121 | 122 | testToFromArrayBuffer :: Effect Unit 123 | testToFromArrayBuffer = do 124 | buf <- run $ 125 | fromArray [ 1, 2, 3 ] 126 | >>= (toArrayBuffer :: buf -> m ArrayBuffer) 127 | >>= (fromArrayBuffer :: ArrayBuffer -> m buf) 128 | >>= toArray 129 | assertEqual { expected: [ 1, 2, 3 ], actual: buf } 130 | 131 | testToString :: Effect Unit 132 | testToString = do 133 | let str = "hello, world" 134 | strOut <- run do 135 | buf <- fromString str ASCII :: m buf 136 | toString ASCII buf 137 | 138 | assertEqual { expected: str, actual: strOut } 139 | 140 | testReadString :: Effect Unit 141 | testReadString = do 142 | let str = "hello, world" 143 | strOut <- run do 144 | buf <- fromString str ASCII :: m buf 145 | readString ASCII 7 12 buf 146 | 147 | assertEqual { expected: "world", actual: strOut } 148 | 149 | testSlice :: Effect Unit 150 | testSlice = do 151 | { bufArr, bufSliceArr } <- run do 152 | buf <- fromArray [ 1, 2, 3, 4 ] :: m buf 153 | let bufSlice = slice 1 3 buf 154 | setAtOffset 42 1 bufSlice 155 | bufArr <- toArray buf 156 | bufSliceArr <- toArray bufSlice 157 | pure { bufArr, bufSliceArr } 158 | 159 | assertEqual { expected: [ 1, 2, 42, 4 ], actual: bufArr } 160 | assertEqual { expected: [ 2, 42 ], actual: bufSliceArr } 161 | 162 | testCopy :: Effect Unit 163 | testCopy = do 164 | { copied, out } <- run do 165 | buf1 <- fromArray [ 1, 2, 3, 4, 5 ] :: m buf 166 | buf2 <- fromArray [ 10, 9, 8, 7, 6 ] 167 | copied <- copy 0 3 buf1 2 buf2 168 | out <- toArray buf2 169 | pure { copied, out } 170 | 171 | assertEqual { expected: 3, actual: copied } 172 | assertEqual { expected: [ 10, 9, 1, 2, 3 ], actual: out } 173 | 174 | testFill :: Effect Unit 175 | testFill = do 176 | out <- run do 177 | buf <- fromArray [ 1, 1, 1, 1, 1 ] :: m buf 178 | fill 42 2 4 buf 179 | toArray buf 180 | 181 | assertEqual { expected: [ 1, 1, 42, 42, 1 ], actual: out } 182 | 183 | testConcat' :: Effect Unit 184 | testConcat' = do 185 | out <- run do 186 | bufs <- traverse (fromArray :: Array Int -> m buf) $ map (\x -> [ x, x + 1, x + 2 ]) [ 0, 3, 6, 9, 12 ] 187 | buf <- concat' bufs 15 188 | toArray buf 189 | 190 | assertEqual { expected: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ], actual: out } 191 | 192 | testGetAtOffset :: Effect Unit 193 | testGetAtOffset = do 194 | { o1, o4, om1 } <- run do 195 | buf <- fromArray [ 1, 2, 3, 4 ] :: m buf 196 | o1 <- getAtOffset 1 buf 197 | o4 <- getAtOffset 4 buf 198 | om1 <- getAtOffset (-1) buf 199 | pure { o1, o4, om1 } 200 | 201 | assertEqual { expected: Just 2, actual: o1 } 202 | assertEqual { expected: Nothing, actual: o4 } 203 | assertEqual { expected: Nothing, actual: om1 } 204 | -------------------------------------------------------------------------------- /test/Test/Node/Buffer/Immutable.purs: -------------------------------------------------------------------------------- 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: "", 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.0, actual: Immutable.read UInt8 6 buf } 86 | 87 | testToString :: Effect Unit 88 | testToString = do 89 | let 90 | str = "hello, world" 91 | str' = Immutable.toString ASCII $ Immutable.fromString str ASCII 92 | assertEqual { expected: str, actual: str' } 93 | 94 | testToArray :: Effect Unit 95 | testToArray = do 96 | assertEqual { expected: [ 1, 2, 3 ], actual: Immutable.toArray buffer123 } 97 | 98 | testReadString :: Effect Unit 99 | testReadString = do 100 | let 101 | str = "hello, world" 102 | str' = Immutable.readString ASCII 7 12 $ Immutable.fromString str ASCII 103 | assertEqual { expected: "world", actual: str' } 104 | 105 | testGetAtOffset :: Effect Unit 106 | testGetAtOffset = do 107 | assertEqual { expected: Just 2, actual: Immutable.getAtOffset 1 buffer123 } 108 | assertEqual { expected: Nothing, actual: Immutable.getAtOffset 99 buffer123 } 109 | assertEqual { expected: Nothing, actual: Immutable.getAtOffset (-1) buffer123 } 110 | 111 | testToFromArrayBuffer :: Effect Unit 112 | testToFromArrayBuffer = do 113 | assertEqual { expected: buffer123, actual: Immutable.fromArrayBuffer $ Immutable.toArrayBuffer buffer123 } 114 | 115 | testConcat' :: Effect Unit 116 | testConcat' = do 117 | let 118 | bufs = map (\x -> Immutable.fromArray [ x, x + 1, x + 2 ]) [ 0, 3, 6, 9, 12 ] 119 | buf = Immutable.concat' bufs 15 120 | out = Immutable.toArray buf 121 | 122 | assertEqual { expected: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ], actual: out } 123 | 124 | testSlice :: Effect Unit 125 | testSlice = do 126 | assertEqual { expected: buffer123, actual: Immutable.slice 0 3 buffer123 } 127 | assertEqual { expected: buffer123, actual: Immutable.slice 0 4 buffer123 } 128 | assertEqual { expected: Immutable.fromArray [ 2 ], actual: Immutable.slice 1 2 buffer123 } 129 | 130 | testSize :: Effect Unit 131 | testSize = do 132 | assertEqual { expected: 0, actual: Immutable.size $ Immutable.fromArray [] } 133 | assertEqual { expected: 3, actual: Immutable.size buffer123 } 134 | -------------------------------------------------------------------------------- /test/Test/Node/Buffer/ST.purs: -------------------------------------------------------------------------------- 1 | module Test.Node.Buffer.ST (test) where 2 | 3 | import Prelude 4 | 5 | import Effect (Effect) 6 | import Effect.Console (log) 7 | import Node.Buffer.Class (create) 8 | import Node.Buffer.Immutable as Immutable 9 | import Node.Buffer.ST (STBuffer, run) 10 | import Test.Assert (assertEqual) 11 | import Test.Node.Buffer.Class (testMutableBuffer) 12 | import Type.Proxy (Proxy(..)) 13 | import Unsafe.Coerce (unsafeCoerce) 14 | 15 | test :: Effect Unit 16 | test = do 17 | log "Testing Node.Buffer.ST ..." 18 | testMutableBuffer (Proxy :: Proxy (STBuffer _)) unsafeCoerce 19 | log " - run" 20 | testRun 21 | 22 | testRun :: Effect Unit 23 | testRun = do 24 | let buf = Immutable.toArray $ run (create 3) 25 | assertEqual { expected: [ 0, 0, 0 ], actual: buf } 26 | --------------------------------------------------------------------------------