diff options
Diffstat (limited to 'RingBuffer.hs')
-rw-r--r-- | RingBuffer.hs | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/RingBuffer.hs b/RingBuffer.hs new file mode 100644 index 0000000..b2779d7 --- /dev/null +++ b/RingBuffer.hs | |||
@@ -0,0 +1,70 @@ | |||
1 | module RingBuffer where | ||
2 | |||
3 | import Control.Monad | ||
4 | import Data.Int | ||
5 | import Data.IORef | ||
6 | |||
7 | data TargetBuffer u = TargetBuffer | ||
8 | { -- | Called whenever the rBack or rSize changes. The purpose is so that | ||
9 | -- some other state, such as an OpenGL vertex buffer object, can remain in | ||
10 | -- sync. | ||
11 | syncBuffer :: (Int32 -> [(Int32,Int32)]) -> IO () | ||
12 | -- | This updates the data associated with a particular index (usually | ||
13 | -- rBack). The update specification /u/ may be a value or a set of | ||
14 | -- key-value pairs or whatever is most convenient way to instruct the | ||
15 | -- target representation to update itself. | ||
16 | , updateBuffer :: Int -> u -> IO () | ||
17 | } | ||
18 | |||
19 | data RingBuffer u = RingBuffer | ||
20 | { rBack :: IORef Int | ||
21 | , rSize :: IORef Int | ||
22 | , ringCapacity :: Int | ||
23 | , rBuffer :: TargetBuffer u | ||
24 | } | ||
25 | |||
26 | newRing :: Int -> IO (TargetBuffer u) -> IO (RingBuffer u) | ||
27 | newRing sz newBuffer = do | ||
28 | backRef <- newIORef 0 | ||
29 | sizeRef <- newIORef 0 | ||
30 | b <- newBuffer | ||
31 | let r = RingBuffer | ||
32 | { rBack = backRef | ||
33 | , rSize = sizeRef | ||
34 | , ringCapacity = sz | ||
35 | , rBuffer = b | ||
36 | } | ||
37 | syncRing r | ||
38 | return r | ||
39 | |||
40 | syncRing :: RingBuffer u -> IO () | ||
41 | syncRing r = do | ||
42 | size <- fromIntegral <$> readIORef (rSize r) | ||
43 | back <- fromIntegral <$> readIORef (rBack r) | ||
44 | let mask 0 = [] | ||
45 | mask cnt | ||
46 | | cnt==size = [(0,cnt)] | ||
47 | | otherwise = case cnt + back - size of | ||
48 | front | front > cnt -> [(front - cnt,size)] | ||
49 | | otherwise -> [(0,back), (front,cnt - front)] | ||
50 | syncBuffer (rBuffer r) mask | ||
51 | |||
52 | clearRing :: RingBuffer u -> IO () | ||
53 | clearRing r = do | ||
54 | writeIORef (rBack r) 0 | ||
55 | writeIORef (rSize r) 0 | ||
56 | syncRing r | ||
57 | |||
58 | pushBack :: RingBuffer u -> u -> IO () | ||
59 | pushBack r upd = do | ||
60 | back <- readIORef $ rBack r | ||
61 | writeIORef (rBack r) (mod (back + 1) (ringCapacity r)) | ||
62 | updateBuffer (rBuffer r) back upd | ||
63 | sz <- readIORef (rSize r) | ||
64 | when (sz < ringCapacity r) $ writeIORef (rSize r) (sz + 1) | ||
65 | syncRing r | ||
66 | |||
67 | updateBack :: RingBuffer u -> u -> IO () | ||
68 | updateBack r upd = do | ||
69 | back <- readIORef $ rBack r | ||
70 | updateBuffer (rBuffer r) back upd | ||