Skip to content

Commit

Permalink
Add replicate and replicateByte to Data.Bytes.Chunks. Release 0.2.13.0
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewthad committed Jan 12, 2024
1 parent 23bb619 commit add05c6
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 19 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Revision history for byteslice

## 0.2.13.0 -- 2024-01-12

* Add `replicate` and `replicateByte` to `Data.Bytes.Chunks`.

## 0.2.12.0 -- 2023-12-14

* Add `Data.Bytes.Chunks.cons`.
Expand Down
2 changes: 1 addition & 1 deletion byteslice.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 2.4
name: byteslice
version: 0.2.12.0
version: 0.2.13.0
synopsis: Slicing managed and unmanaged memory
description:
This library provides types that allow the user to talk about a slice of
Expand Down
18 changes: 2 additions & 16 deletions src/Data/Bytes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ module Data.Bytes
, singleton
, doubleton
, tripleton
, replicate
, Pure.replicate
-- ** Unsliced
, singletonU
, doubletonU
, tripletonU
, replicateU
, Pure.replicateU
-- * Filtering
, takeWhile
, dropWhile
Expand Down Expand Up @@ -314,20 +314,6 @@ tripletonU !a !b !c = runByteArrayST do
PM.writeByteArray arr 2 c
PM.unsafeFreezeByteArray arr

-- | Replicate a byte @n@ times.
replicate ::
Int -- ^ Desired length @n@
-> Word8 -- ^ Byte to replicate
-> Bytes
replicate !n !w = Bytes (replicateU n w) 0 n

-- | Variant of 'replicate' that returns a unsliced byte array.
replicateU :: Int -> Word8 -> ByteArray
replicateU !n !w = runByteArrayST do
arr <- PM.newByteArray n
PM.setByteArray arr 0 n w
PM.unsafeFreezeByteArray arr

-- | /O(n)/ Return the suffix of the second string if its prefix
-- matches the entire first string.
stripPrefix :: Bytes -> Bytes -> Maybe Bytes
Expand Down
48 changes: 47 additions & 1 deletion src/Data/Bytes/Chunks.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ module Data.Bytes.Chunks
, concatByteString
, reverse
, reverseOnto
, replicate
, replicateByte
-- * Folds
, foldl'
-- * Splitting
Expand All @@ -48,7 +50,7 @@ module Data.Bytes.Chunks
, writeFile
) where

import Prelude hiding (Foldable(..),concat,reverse,readFile,writeFile)
import Prelude hiding (Foldable(..),concat,reverse,readFile,writeFile,replicate)

import Control.Exception (IOException,catch)
import Control.Monad.ST.Run (runIntByteArrayST)
Expand Down Expand Up @@ -95,6 +97,50 @@ cons :: Bytes -> Chunks -> Chunks
{-# inline cons #-}
cons = ChunksCons

-- | Repeat the byte sequence over and over. Returns empty chunks when given
-- a negative repetition count.
replicate ::
Bytes
-> Int -- ^ Number of times to repeat the sequence.
-> Chunks
replicate !b@(Bytes _ _ len) !n
| n <= 0 = ChunksNil
| len == 0 = ChunksNil
| otherwise = go n ChunksNil
where
-- Implementation note: We do not have to reverse the chunks at the end.
go i !acc = case i of
0 -> acc
_ -> go (i - 1) (ChunksCons b acc)

-- | Repeat the byte over and over. This builds a single byte array that
-- is at most 64KiB and shares that across every @ChunksCons@ cell.
--
-- An as example, creating a 2GiB chunks this way would use 64KiB for the
-- byte array, and there would be the additional overhead of the 2^15
-- @ChunksCons@ data constructors. On a 64-bit platform, @ChunksCons@
-- takes 40 bytes, so the total memory consumption would be
-- @2^16 + 40 * 2^15@, which is roughly 1.37MB. The same reasoning
-- shows that it takes about 83.95MB to represent a 128GiB chunks.
--
-- The size of the shared payload is an implementation detail. Do not
-- rely on this function producing 64KiB chunks. The implementation might
-- one day change to something smarter that minimizes the memory footprint
-- for very large chunks.
replicateByte ::
Word8
-> Int -- ^ Number of times to replicate the byte
-> Chunks
replicateByte !w !n
| n <= 0 = ChunksNil
| n < 65536 = ChunksCons (Bytes.replicate n w) ChunksNil
| otherwise = go (Bytes.replicateU 65536 w) n ChunksNil
where
go !shared !remaining !acc
| remaining == 0 = acc
| remaining < 65536 = ChunksCons (Bytes shared 0 remaining) acc
| otherwise = go shared (remaining - 65536) (ChunksCons (Bytes shared 0 65536) acc)

-- | Are there any bytes in the chunked byte sequences?
null :: Chunks -> Bool
null = go where
Expand Down
17 changes: 16 additions & 1 deletion src/Data/Bytes/Pure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ module Data.Bytes.Pure
, mapU
, null
, toShortByteString
, replicate
, replicateU
) where

import Prelude hiding (Foldable(..),map)
import Prelude hiding (Foldable(..),map,replicate)

import Control.Monad.Primitive (PrimState,PrimMonad)
import Control.Monad.ST.Run (runByteArrayST)
Expand Down Expand Up @@ -346,3 +348,16 @@ toShortByteString :: Bytes -> ShortByteString
toShortByteString !b = case toByteArray b of
PM.ByteArray x -> SBS x

-- | Replicate a byte @n@ times.
replicate ::
Int -- ^ Desired length @n@
-> Word8 -- ^ Byte to replicate
-> Bytes
replicate !n !w = Bytes (replicateU n w) 0 n

-- | Variant of 'replicate' that returns a unsliced byte array.
replicateU :: Int -> Word8 -> ByteArray
replicateU !n !w = runByteArrayST do
arr <- PM.newByteArray n
PM.setByteArray arr 0 n w
PM.unsafeFreezeByteArray arr

0 comments on commit add05c6

Please sign in to comment.