1{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE RecordWildCards #-}
3{-# LANGUAGE BangPatterns #-}
4{-# LANGUAGE ExistentialQuantification #-}
7-- Module : Streamly.Memory.Ring
8-- Copyright : (c) 2019 Composewell Technologies
10-- Maintainer : streamly@composewell.com
11-- Stability : experimental
15module Streamly.Memory.Ring
29 -- * Fast Byte Comparisons
34import Control.Exception (assert)
35import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr)
36import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
37import Foreign.Ptr (plusPtr, minusPtr, castPtr)
38import Foreign.Storable (Storable(..))
39import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)
40import GHC.Ptr (Ptr(..))
41import Prelude hiding (length, concat)
43import Control.Monad.IO.Class (MonadIO(..))
45import qualified Streamly.Internal.Memory.Array.Types as A
47-- | A ring buffer is a mutable array of fixed size. Initially the array is
48-- empty, with ringStart pointing at the start of allocated memory. We call the
49-- next location to be written in the ring as ringHead. Initially ringHead ==
50-- ringStart. When the first item is added, ringHead points to ringStart +
51-- sizeof item. When the buffer becomes full ringHead would wrap around to
52-- ringStart. When the buffer is full, ringHead always points at the oldest
53-- item in the ring and the newest item added always overwrites the oldest
56-- When using it we should keep in mind that a ringBuffer is a mutable data
57-- structure. We should not leak out references to it for immutable use.
60 { ringStart :: !(ForeignPtr a) -- first address
61 , ringBound :: !(Ptr a) -- first address beyond allocated memory
64-- | Create a new ringbuffer and return the ring buffer and the ringHead.
65-- Returns the ring and the ringHead, the ringHead is same as ringStart.
67new :: forall a. Storable a => Int -> IO (Ring a, Ptr a)
69 let size = count * sizeOf (undefined :: a)
70 fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))
71 let p = unsafeForeignPtrToPtr fptr
74 , ringBound = p `plusPtr` size
77-- | Advance the ringHead by 1 item, wrap around if we hit the end of the
80advance :: forall a. Storable a => Ring a -> Ptr a -> Ptr a
81advance Ring{..} ringHead =
82 let ptr = ringHead `plusPtr` sizeOf (undefined :: a)
85 else unsafeForeignPtrToPtr ringStart
87-- | Insert an item at the head of the ring, when the ring is full this
88-- replaces the oldest item in the ring with the new item. This is unsafe
89-- beause ringHead supplied is not verified to be within the Ring. Also,
90-- the ringStart foreignPtr must be guaranteed to be alive by the caller.
91{-# INLINE unsafeInsert #-}
92unsafeInsert :: Storable a => Ring a -> Ptr a -> a -> IO (Ptr a)
93unsafeInsert rb ringHead newVal = do
95 -- touchForeignPtr (ringStart rb)
96 return $ advance rb ringHead
98-- XXX remove all usage of A.unsafeInlineIO
100-- | Like 'unsafeEqArray' but compares only N bytes instead of entire length of
101-- the ring buffer. This is unsafe because the ringHead Ptr is not checked to
103{-# INLINE unsafeEqArrayN #-}
104unsafeEqArrayN :: Ring a -> Ptr a -> A.Array a -> Int -> Bool
105unsafeEqArrayN Ring{..} rh A.Array{..} n =
106 let !res = A.unsafeInlineIO $ do
107 let rs = unsafeForeignPtrToPtr ringStart
108 let as = unsafeForeignPtrToPtr aStart
109 assert (aBound `minusPtr` as >= ringBound `minusPtr` rs) (return ())
110 let len = ringBound `minusPtr` rh
111 r1 <- A.memcmp (castPtr rh) (castPtr as) (min len n)
113 then A.memcmp (castPtr rs) (castPtr (as `plusPtr` len))
114 (min (rh `minusPtr` rs) (n - len))
116 -- XXX enable these, check perf impact
117 -- touchForeignPtr ringStart
118 -- touchForeignPtr aStart
122-- | Byte compare the entire length of ringBuffer with the given array,
123-- starting at the supplied ringHead pointer. Returns true if the Array and
124-- the ringBuffer have identical contents.
126-- This is unsafe because the ringHead Ptr is not checked to be in range. The
127-- supplied array must be equal to or bigger than the ringBuffer, ARRAY BOUNDS
129{-# INLINE unsafeEqArray #-}
130unsafeEqArray :: Ring a -> Ptr a -> A.Array a -> Bool
131unsafeEqArray Ring{..} rh A.Array{..} =
132 let !res = A.unsafeInlineIO $ do
133 let rs = unsafeForeignPtrToPtr ringStart
134 let as = unsafeForeignPtrToPtr aStart
135 assert (aBound `minusPtr` as >= ringBound `minusPtr` rs)
137 let len = ringBound `minusPtr` rh
138 r1 <- A.memcmp (castPtr rh) (castPtr as) len
139 r2 <- A.memcmp (castPtr rs) (castPtr (as `plusPtr` len))
141 -- XXX enable these, check perf impact
142 -- touchForeignPtr ringStart
143 -- touchForeignPtr aStart
149-- | Fold the buffer starting from ringStart up to the given 'Ptr' using a pure
150-- step function. This is useful to fold the items in the ring when the ring is
151-- not full. The supplied pointer is usually the end of the ring.
153-- Unsafe because the supplied Ptr is not checked to be in range.
154{-# INLINE unsafeFoldRing #-}
155unsafeFoldRing :: forall a b. Storable a
156 => Ptr a -> (b -> a -> b) -> b -> Ring a -> b
157unsafeFoldRing ptr f z Ring{..} =
158 let !res = A.unsafeInlineIO $ withForeignPtr ringStart $ \p ->
163 | p == q = return acc
166 go (f acc x) (p `plusPtr` sizeOf (undefined :: a)) q
168-- XXX Can we remove MonadIO here?
169withForeignPtrM :: MonadIO m => ForeignPtr a -> (Ptr a -> m b) -> m b
170withForeignPtrM fp fn = do
171 r <- fn $ unsafeForeignPtrToPtr fp
172 liftIO $ touchForeignPtr fp
175-- | Like unsafeFoldRing but with a monadic step function.
176{-# INLINE unsafeFoldRingM #-}
177unsafeFoldRingM :: forall m a b. (MonadIO m, Storable a)
178 => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b
179unsafeFoldRingM ptr f z Ring {..} =
180 withForeignPtrM ringStart $ \x -> go z x ptr
183 | start == end = return acc
185 let !x = A.unsafeInlineIO $ peek start
187 go acc' (start `plusPtr` sizeOf (undefined :: a)) end
189-- | Fold the entire length of a ring buffer starting at the supplied ringHead
190-- pointer. Assuming the supplied ringHead pointer points to the oldest item,
191-- this would fold the ring starting from the oldest item to the newest item in
193{-# INLINE unsafeFoldRingFullM #-}
194unsafeFoldRingFullM :: forall m a b. (MonadIO m, Storable a)
195 => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b
196unsafeFoldRingFullM rh f z rb@Ring {..} =
197 withForeignPtrM ringStart $ \_ -> go z rh
200 let !x = A.unsafeInlineIO $ peek start
202 let ptr = advance rb start
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list