Loading...

Streamly.Internal.Data.RingArray

Unboxed, mutable ring arrays of fixed size. In case you need to expand the size of a ring, copy it to a MutArray, expand the array and cast it back to ring.

Documentation

data RingArray a Source #

A ring buffer is a circular buffer. A new element is inserted at a position called the ring head which points to the oldest element in the ring, an insert overwrites the oldest element. After inserting, the head is moved to point to the next element which is now the oldest element.

Elements in the ring are indexed relative to the head. RingArray head is designated as the index 0 of the ring buffer, it points to the oldest or the first element in the buffer. Higher positive indices point to the newer elements in the buffer. Index -1 points to the newest or the last element in the buffer. Higher negative indices point to older elements.

The ring is of fixed size and cannot be expanded or reduced after creation. Creation of zero sized rings is not allowed.

This module provides an unboxed implementation of ring buffers for best performance.

Constructors

RingArray 

type Ring = RingArray Source #

Deprecated: Please use RingArray instead.

Debugging

showRing :: (Unbox a, Show a) => RingArray a -> IO String Source #

Show the contents of a RingArray as a list.

>>> showRing rb = RingArray.toList rb >>= return . show

Construction

createOfLast :: (Unbox a, MonadIO m) => Int -> Fold m a (RingArray a) Source #

createOfLast n returns the last n elements of the stream in a ring array. n must be non-zero.

castMutArray :: forall a. Unbox a => MutArray a -> Maybe (RingArray a) Source #

Cast a MutArray to a ring sharing the same memory without copying. The ring head is at index 0 of the array. Cast fails with Nothing if the array is a slice.

>>> castMutArray = RingArray.castMutArrayWith 0

castMutArrayWith :: forall a. Unbox a => Int -> MutArray a -> Maybe (RingArray a) Source #

castMutArray arr index casts a mutable array to a ring array having the ring head at index position in the array.

This operation throws an error if the index is not within the array bounds. It returns Nothing if the array cannot be cast into ring because the array is a slice. In that case clone the array and cast it or stream the array and use createOfLast to create a ring.

unsafeCastMutArray :: forall a. Unbox a => MutArray a -> RingArray a Source #

Cast a MutArray to a ring sharing the same memory without copying. The ring head is at index 0 of the array. The array must not be a slice.

>>> unsafeCastMutArray = RingArray.unsafeCastMutArrayWith 0

unsafeCastMutArrayWith :: forall a. Unbox a => Int -> MutArray a -> RingArray a Source #

The array must not be a slice, and the index must be within the bounds of the array otherwise unpredictable behavior will occur.

Moving the Head

moveForward :: forall a. Unbox a => RingArray a -> RingArray a Source #

Advance the ring head forward by 1 slot, the ring head will now point to the next (newer) item, and the old ring head position will become the latest or the newest item position.

>>> moveForward = RingArray.moveBy 1

moveReverse :: forall a. Unbox a => RingArray a -> RingArray a Source #

Move the ring head backward by 1 slot, the ring head will now point to the prev (older) item, when the ring head is at the oldest item it will move to the newest item.

>>> moveForward = RingArray.moveBy (-1)

moveBy :: forall a. Unbox a => Int -> RingArray a -> RingArray a Source #

Move the ring head forward or backward by n slots. Moves forward if the argument is positive and backward if it is negative.

Throws an error if the absolute value of count is more than or euqal to the ring size.

In-place Mutation

insert :: RingArray a -> a -> m (RingArray a) Source #

Insert a new element without replacing an old one. Expands the size of the ring. This is similar to the snoc operation for MutArray.

Unimplemented

replace :: forall m a. (MonadIO m, Unbox a) => RingArray a -> a -> m (RingArray a, a) Source #

Replace the oldest item in the ring (the item at the ring head) with a new item and move the ring head to the remaining oldest item.

Throws an error if the ring is empty.

replace_ :: forall m a. (MonadIO m, Unbox a) => RingArray a -> a -> m (RingArray a) Source #

Like replace but does not return the old value of overwritten element.

Same as:

>>> replace_ rb x = RingArray.putIndex 0 rb x >> pure (RingArray.moveForward rb)

putIndex :: forall m a. (MonadIO m, Unbox a) => Int -> RingArray a -> a -> m () Source #

O(1) Write the given element at the given index relative to the current position of the ring head. Index starts at 0, could be positive or negative.

Throws an error if the index is more than or equal to the size of the ring.

Performs in-place mutation of the array.

modifyIndex :: Int -> RingArray a -> (a -> (a, b)) -> m b Source #

Modify a given index of a ring array using a modifier function.

Unimplemented

Random Access

getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> RingArray a -> m (Maybe a) Source #

O(1) Lookup the element at the given index relative to the ring head. Index starts from 0, could be positive or negative. Returns Nothing if the index is more than or equal to the size of the ring.

unsafeGetIndex :: forall m a. (MonadIO m, Unbox a) => Int -> RingArray a -> m a Source #

Like getIndex but does not check the bounds. Unpredictable behavior occurs if the index is more than or equal to the ring size.

unsafeGetHead :: (MonadIO m, Unbox a) => RingArray a -> m a Source #

O(1) Lookup the element at the head position.

Prefer this over unsafeGetIndex 0 as it does not have have to perform an index rollover check.

Conversion

toList :: (MonadIO m, Unbox a) => RingArray a -> m [a] Source #

Copy the ring to a list, the first element of the list is the oldest element of the ring (i.e. ring head) and the last is the newest.

>>> toList = Stream.toList . RingArray.read

toMutArray :: (MonadIO m, Unbox a) => RingArray a -> m (MutArray a) Source #

Copy the ring to a MutArray, the first element of the MutArray is the oldest element of the ring (i.e. ring head) and the last is the newest.

>>> toMutArray rb = Stream.fold (MutArray.createOf (RingArray.length rb)) $ RingArray.read rb

Streams

read :: forall m a. (MonadIO m, Unbox a) => RingArray a -> Stream m a Source #

Read the entire ring as a stream, starting at the ring head i.e. from oldest to newest.

readRev :: forall m a. (MonadIO m, Unbox a) => RingArray a -> Stream m a Source #

Read the entire ring as a stream, starting from newest to oldest elements.

Unfolds

reader :: forall m a. (MonadIO m, Unbox a) => Unfold m (RingArray a) a Source #

Read the entire ring, starting at the ring head i.e. from oldest to newest.

readerRev :: forall m a. (MonadIO m, Unbox a) => Unfold m (RingArray a) a Source #

Read the entire ring in reverse order, starting at the item before the ring head i.e. from newest to oldest

Size

length :: forall a. Unbox a => RingArray a -> Int Source #

O(1) Get the length of the ring. i.e. the number of elements in the ring.

byteLength :: RingArray a -> Int Source #

O(1) Get the byte length of the ring.

Casting

cast :: forall a b. Unbox b => RingArray a -> Maybe (RingArray b) Source #

Cast a ring having elements of type a into a ring having elements of type b. The length of the ring should be a multiple of the size of the target element otherwise Nothing is returned.

unsafeCast :: RingArray a -> RingArray b Source #

Cast a ring having elements of type a into a ring having elements of type b. The ring size must be a multiple of the size of type b.

asBytes :: RingArray a -> RingArray Word8 Source #

Cast a RingArray a into a RingArray Word8.

asMutArray :: RingArray a -> (MutArray a, Int) Source #

Cast the ring to a mutable array. Return the mutable array as well as the current position of the ring head. Note that the array does not start with the current ring head. The array refers to the same memory as the ring.

Folds

foldlM' :: forall m a b. (MonadIO m, Unbox a) => (b -> a -> m b) -> b -> RingArray a -> m b Source #

Fold the entire length of a ring buffer starting at the current ring head.

Note, this will crash on ring of 0 size.

fold :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> RingArray a -> m b Source #

Fold the entire length of a ring buffer starting at the current ring head.

Stream of Rings

ringsOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (RingArray a) Source #

ringsOf n stream groups the input stream into a stream of ring arrays of size up to n. See scanRingsOf for more details.

scanRingsOf :: forall m a. (MonadIO m, Unbox a) => Int -> Scanl m a (RingArray a) Source #

scanRingsOf n groups the input stream into a stream of ring arrays of size up to n. The first ring would be of size 1, then 2, and so on up to size n, when size n is reached the ring starts sliding out the oldest elements and keeps the newest n elements.

Note that the ring emitted is a mutable reference, therefore, should not be retained without copying otherwise the contents will change in the next iteration of the stream.

scanCustomFoldRingsBy :: forall m a b. (MonadIO m, Unbox a) => (RingArray a -> m b) -> Int -> Scanl m a b Source #

scanFoldRingsBy :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Int -> Scanl m a b Source #

Apply the given fold on sliding windows of the given size. Note that this could be expensive because each operation goes through the entire window. This should be used only if there is no efficient alternative way possible.

Examples:

>>> windowRange = RingArray.scanFoldRingsBy Fold.range
>>> windowMinimum = RingArray.scanFoldRingsBy Fold.minimum
>>> windowMaximum = RingArray.scanFoldRingsBy Fold.maximum

Fast Byte Comparisons

eqArray :: RingArray a -> Array a -> IO Bool Source #

Byte compare the entire length of ringBuffer with the given array, starting at the supplied ring head index. Returns true if the Array and the ring have identical contents. If the array is bigger checks only up to the ring length. If array is shorter than then ring, it is treated as an error.

eqArrayN :: RingArray a -> Array a -> Int -> IO Bool Source #

Like eqArray but compares only N bytes instead of entire length of the ring buffer. If N is bigger than the ring or array size, it is treated as an error.

Deprecated

unsafeFoldRing :: forall a b. Unbox a => Int -> (b -> a -> b) -> b -> RingArray a -> IO b Source #

Deprecated: This function will be removed in future.

Fold the buffer starting from ringStart up to the given index using a pure step function. This is useful to fold the items in the ring when the ring is not full. The supplied index is usually the end of the ring.

Unsafe because the supplied index is not checked to be in range.

unsafeFoldRingM :: forall m a b. (MonadIO m, Unbox a) => Int -> (b -> a -> m b) -> b -> RingArray a -> m b Source #

Deprecated: This function will be removed in future.

Like unsafeFoldRing but with a monadic step function.

unsafeFoldRingNM :: forall m a b. (MonadIO m, Unbox a) => Int -> (b -> a -> m b) -> b -> RingArray a -> m b Source #

Deprecated: This function will be removed in future.

Fold n items in the ring starting at the ring head. Won't fold more than the length of the ring even if n is larger.

Note, this will crash on ring of 0 size.

unsafeFoldRingFullM :: forall m a b. (MonadIO m, Unbox a) => (b -> a -> m b) -> b -> RingArray a -> m b Source #

Deprecated: This function will be removed in future.

slidingWindow :: forall m a b. (MonadIO m, Unbox a) => Int -> Fold m (a, Maybe a) b -> Fold m a b Source #

Deprecated: Please use Scanl.incrScan instead.

slidingWindow collector is an incremental sliding window fold that does not require all the intermediate elements in a computation. This maintains n elements in the window, when a new element comes it slides out the oldest element and the new element along with the old element are supplied to the collector fold.

The Maybe type is for the case when initially the window is filling and there is no old element.

slidingWindowWith :: forall m a b. (MonadIO m, Unbox a) => Int -> Fold m ((a, Maybe a), m (MutArray a)) b -> Fold m a b Source #

Deprecated: Please use Scanl.incrScanWith instead.

Like slidingWindow but also provides the entire ring contents as an Array. The array reflects the state of the ring after inserting the incoming element.

IMPORTANT NOTE: The ring is mutable, therefore, the result of (m (Array a)) action depends on when it is executed. It does not capture the sanpshot of the ring at a particular time.