Skip to content

Commit 9de9efe

Browse files
Add RingArray module
1 parent 070eccb commit 9de9efe

File tree

4 files changed

+119
-17
lines changed

4 files changed

+119
-17
lines changed

core/src/Streamly/Data/Array.hs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ module Streamly.Data.Array
107107
)
108108
where
109109

110-
#include "inline.hs"
111-
112110
import Streamly.Internal.Data.Array
113111
import Streamly.Internal.Data.MutByteArray (Unbox(..), Serialize(..))
114112

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
{-# LANGUAGE CPP #-}
2+
-- |
3+
-- Module : Streamly.Data.RingArray
4+
-- Copyright : (c) 2025 Composewell Technologies
5+
--
6+
-- License : BSD3
7+
-- Maintainer : streamly@composewell.com
8+
-- Stability : released
9+
-- Portability : GHC
10+
--
11+
-- This module provides APIs to create and use unboxed, mutable ring arrays of
12+
-- fixed size. Ring arrays are useful to keep a circular buffer or a sliding
13+
-- window of elements.
14+
--
15+
-- RingArrays are of fixed size but there is a way to expand the size of the
16+
-- ring, you can copy the ring to a MutArray, expand the MutArray and the cast
17+
-- it back to RingArray.
18+
--
19+
-- This module is designed to be imported qualified:
20+
--
21+
-- >>> import qualified Streamly.Data.RingArray as Ring
22+
--
23+
-- Please refer to "Streamly.Internal.Data.RingArray" for functions that have
24+
-- not yet been released.
25+
--
26+
27+
module Streamly.Data.RingArray
28+
( RingArray
29+
30+
-- * Construction
31+
, createOfLast
32+
, castMutArray -- XXX this is unsafeFreeze in Array module
33+
, castMutArrayWith
34+
-- , unsafeCastMutArray
35+
-- , unsafeCastMutArrayWith
36+
37+
-- * Moving the Head
38+
, moveForward
39+
, moveReverse
40+
-- , moveBy
41+
42+
-- * In-place Mutation
43+
, insert
44+
, replace
45+
, replace_
46+
, putIndex
47+
, modifyIndex
48+
49+
-- * Random Access
50+
, getIndex
51+
, unsafeGetIndex
52+
, unsafeGetHead
53+
54+
-- * Conversion
55+
, toList
56+
, toMutArray
57+
58+
-- * Streams
59+
, read
60+
, readRev
61+
62+
-- * Unfolds
63+
, reader
64+
, readerRev
65+
66+
-- * Size
67+
, length
68+
, byteLength
69+
70+
-- * Casting
71+
, cast
72+
-- , unsafeCast
73+
, asBytes
74+
, asMutArray
75+
-- , asMutArray_
76+
77+
-- * Folds
78+
-- , foldlM'
79+
, fold
80+
81+
-- * Stream of Rings
82+
, ringsOf
83+
, scanRingsOf
84+
85+
-- * Fast Byte Comparisons
86+
, eqArray
87+
, eqArrayN
88+
89+
) where
90+
91+
import Streamly.Internal.Data.RingArray
92+
import Prelude hiding (read, length)

core/src/Streamly/Internal/Data/RingArray.hs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -285,16 +285,22 @@ unsafeCastMutArray = unsafeCastMutArrayWith 0
285285
-- field in the ring. For copying we can have another API though.
286286

287287
-- XXX castMutArray is called unsafeFreeze in the Array module. Make the naming
288-
-- consistent? Also we can use castMutArrayWith to specify the index and use
289-
-- the default index 0.
288+
-- consistent?
290289

291-
-- | @castMutArray arr index@ casts a mutable array to a ring array having
292-
-- the ring head at @index@ position in the array.
290+
-- | @castMutArrayWith index arr@ casts a mutable array to a ring array, and
291+
-- positions the ring head at the given @index@ in the array.
292+
--
293+
-- A MutArray can be a slice which means its memory starts from some offset in
294+
-- the underlying MutableByteArray, and not from 0 offset. RingArray always
295+
-- uses the memory from offset zero in the MutableByteArray, therefore, it
296+
-- refuses to cast if it finds the array does not start from offset zero i.e.
297+
-- if the array was created from some slicing operation over another array. In
298+
-- such cases it returns 'Nothing'.
299+
--
300+
-- To create a RingArray from a sliced MutArray use 'createOfLast', or clone
301+
-- the MutArray and then cast it.
293302
--
294303
-- This operation throws an error if the index is not within the array bounds.
295-
-- It returns Nothing if the array cannot be cast into ring because the array
296-
-- is a slice. In that case clone the array and cast it or stream the array and
297-
-- use 'createOfLast' to create a ring.
298304
--
299305
{-# INLINE castMutArrayWith #-}
300306
castMutArrayWith :: forall a. Unbox a => Int -> MutArray a -> Maybe (RingArray a)
@@ -306,8 +312,10 @@ castMutArrayWith i arr
306312
| otherwise = Nothing
307313

308314
-- | Cast a MutArray to a ring sharing the same memory without copying. The
309-
-- ring head is at index 0 of the array. Cast fails with Nothing if the array
310-
-- is a slice.
315+
-- ring head is positioned at index 0 of the array. The size of the ring is
316+
-- equal to the MutArray length.
317+
--
318+
-- See 'castMutArrayWith' for failure scenarios.
311319
--
312320
-- >>> castMutArray = RingArray.castMutArrayWith 0
313321
--
@@ -667,7 +675,8 @@ eqArrayN RingArray{..} Array.Array{..} nBytes
667675
{-# INLINE eqArray #-}
668676
eqArray :: RingArray a -> Array a -> IO Bool
669677
eqArray RingArray{..} Array.Array{..}
670-
| arrEnd - arrStart < ringSize = error "eqArrayN: array is shorter than ring"
678+
| arrEnd - arrStart < ringSize =
679+
error "eqArrayN: array is shorter than ring"
671680
| otherwise = do
672681
part1 <-
673682
MutByteArray.unsafeByteCmp
@@ -832,6 +841,10 @@ asMutArray rb =
832841
, ringHead rb
833842
)
834843

844+
-- | Like 'asMutArray' but does not return the ring head.
845+
--
846+
-- >>> asMutArray_ = fst . RingArray.asMutArray
847+
--
835848
{-# INLINE asMutArray_ #-}
836849
asMutArray_ :: RingArray a -> MutArray a
837850
asMutArray_ rb =

core/streamly-core.cabal

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,6 @@ library
455455

456456
-- Pre-release modules
457457
-- , Streamly.Data.Pipe
458-
-- , Streamly.Data.RingArray
459458
-- , Streamly.Data.RingArray.Generic
460459
-- , Streamly.Data.IORef
461460
-- , Streamly.Data.List
@@ -466,18 +465,18 @@ library
466465
-- , Streamly.Data.Either.Strict
467466

468467
-- streamly-core released modules in alphabetic order
469-
-- NOTE: these must be added to streamly.cabal as well
470468
, Streamly.Console.Stdio
471469
, Streamly.Control.Exception
472-
, Streamly.Data.MutByteArray
473470
, Streamly.Data.Array
474471
, Streamly.Data.Array.Generic
472+
, Streamly.Data.Fold
475473
, Streamly.Data.MutArray
476474
, Streamly.Data.MutArray.Generic
477-
, Streamly.Data.Fold
478-
, Streamly.Data.Scanl
475+
, Streamly.Data.MutByteArray
479476
, Streamly.Data.Parser
480477
, Streamly.Data.ParserK
478+
, Streamly.Data.RingArray
479+
, Streamly.Data.Scanl
481480
, Streamly.Data.Stream
482481
, Streamly.Data.StreamK
483482
, Streamly.Data.Unfold

0 commit comments

Comments
 (0)