{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
module Haskus.Memory.View
( View (..)
, ViewSource (..)
, ViewPattern (..)
, viewReadWord8
, newBufferView
, newBufferWeakView
, newViewWeakView
, copyBufferWithPattern
, viewToBuffer
, showViewState
, patternSize
, unsafePatternSize
)
where
import Data.IORef
import System.Mem.Weak
import Control.Concurrent
import Haskus.Utils.Monad
import Haskus.Number.Word
import Haskus.Memory.Buffer
data ViewSource
= forall pin fin heap. SourceBuffer (Buffer 'Immutable pin fin heap)
| forall pin fin heap. SourceWeakBuffer (Weak (Buffer 'Immutable pin fin heap))
| SourceWeakView (Weak ViewIORef)
newtype View = View ViewIORef
type ViewIORef = IORef (ViewSource,ViewPattern)
data ViewPattern
= PatternFull
| Pattern1D
{ pattern1DOffset :: {-# UNPACK #-} !Word
, pattern1DSize :: {-# UNPACK #-} !Word
}
| Pattern2D
{ pattern2DOffset :: {-# UNPACK #-} !Word
, pattern2DWidth :: {-# UNPACK #-} !Word
, pattern2DHeight :: {-# UNPACK #-} !Word
, pattern2DStride :: {-# UNPACK #-} !Word
}
| PatternOn ViewPattern ViewPattern
deriving (Show)
patternOffset :: ViewPattern -> Word -> Word
patternOffset pat off = case pat of
PatternFull -> off
Pattern1D off2 _sz -> off2+off
Pattern2D off2 w _h stride -> let (y,x) = off `quotRem` w in off2+y*(w+stride)+x
PatternOn p1 p2 -> patternOffset p2 (patternOffset p1 off)
unsafePatternSize :: ViewPattern -> Word
unsafePatternSize = \case
PatternFull -> error "Don't call unsafePatternSize on PatternFull"
Pattern1D _off sz -> sz
Pattern2D _off w h _stride -> w * h
PatternOn p1 _p2 -> unsafePatternSize p1
patternSize :: ViewPattern -> Word -> Word
patternSize v bsz = case v of
PatternFull -> bsz
Pattern1D _off sz -> sz
Pattern2D _off w h _stride -> w * h
PatternOn p1 p2 -> patternSize p1 (patternSize p2 bsz)
patternApplyOn :: ViewPattern -> ViewPattern -> ViewPattern
patternApplyOn p1 p2 = case (p1, p2) of
(PatternFull,p) -> p
(p,PatternFull) -> p
(Pattern1D o1 s1, Pattern1D o2 _s2) -> Pattern1D (o1+o2) s1
_ -> PatternOn p1 p2
viewReadWord8 :: MonadIO m => View -> Word -> m Word8
viewReadWord8 view off =
withValidView view
(\b pat -> bufferReadWord8IO b (patternOffset pat off))
(\b pat -> bufferReadWord8IO b (patternOffset pat off))
(\v pat -> viewReadWord8 v (patternOffset pat off))
withValidView
:: MonadIO m
=> View
-> (forall pin fin heap. Buffer 'Immutable pin fin heap -> ViewPattern -> m a)
-> (forall pin fin heap. Buffer 'Immutable pin fin heap -> ViewPattern -> m a)
-> (View -> ViewPattern -> m a)
-> m a
withValidView (View ref) fb fwb fwv = go True
where
go _firstRun = do
(src,pat) <- liftIO (readIORef ref)
let waitForSource = do
liftIO yield
go False
case src of
SourceBuffer b -> fb b pat
SourceWeakBuffer wb -> liftIO (deRefWeak wb) >>= \case
Nothing -> waitForSource
Just b -> fwb b pat
SourceWeakView wv -> liftIO (deRefWeak wv) >>= \case
Nothing -> waitForSource
Just v2 -> fwv (View v2) pat
newBufferView :: MonadIO m => Buffer 'Immutable pin fin heap -> ViewPattern -> m View
newBufferView b pat = View <$> liftIO (newIORef (SourceBuffer b,pat))
newBufferWeakView :: MonadIO m => Buffer 'Immutable pin fin heap -> ViewPattern -> m View
newBufferWeakView b pat = do
v <- View <$> (liftIO $ newIORef (SourceBuffer b,pat))
assignBufferWeakView v b pat
return v
assignBufferWeakView
:: MonadIO m
=> View
-> Buffer 'Immutable pin fin heap
-> ViewPattern
-> m ()
assignBufferWeakView (View ref) b pat = do
wViewRef <- liftIO $ mkWeakIORef ref (return ())
let finalizer = bufferWeakViewFinalier b pat wViewRef
wb <- liftIO (mkWeakPtr b (Just finalizer))
liftIO (writeIORef ref (SourceWeakBuffer wb,pat))
bufferWeakViewFinalier
:: Buffer 'Immutable pin fin heap
-> ViewPattern
-> Weak ViewIORef
-> IO ()
bufferWeakViewFinalier b pat wViewRef = deRefWeak wViewRef >>= \case
Nothing -> return ()
Just viewRef -> do
bsz <- bufferSizeIO b
newSrc <- case pat of
PatternFull -> return (SourceBuffer b)
Pattern1D 0 psz | psz == bsz -> return (SourceBuffer b)
Pattern2D 0 w h 0 | w*h == bsz -> return (SourceBuffer b)
Pattern2D _ w h _ | w == 0 || h == 0 -> error "Invalid Pattern2D: width or height set to 0"
_ -> do
b' <- copyBufferWithPattern b pat
b'' <- unsafeBufferFreeze b'
return (SourceBuffer b'')
writeIORef viewRef (newSrc,PatternFull)
newViewWeakView :: MonadIO m => View -> ViewPattern -> m View
newViewWeakView src@(View srcRef) pat = do
v <- liftIO $ do
(srcSrc,srcPat) <- readIORef srcRef
View <$> newIORef (srcSrc, pat `patternApplyOn` srcPat)
assignViewWeakView v src pat
return v
assignViewWeakView :: MonadIO m => View -> View -> ViewPattern -> m ()
assignViewWeakView (View ref) (View srcRef) pat = do
weakView <- liftIO $ mkWeakIORef ref (return ())
let finalizer = viewWeakViewFinalizer weakView srcRef pat
wSrcRef <- liftIO $ mkWeakIORef srcRef finalizer
liftIO (writeIORef ref (SourceWeakView wSrcRef,pat))
liftIO (touch srcRef)
viewWeakViewFinalizer :: Weak ViewIORef -> ViewIORef -> ViewPattern -> IO ()
viewWeakViewFinalizer weakView srcRef pat = deRefWeak weakView >>= \case
Nothing -> return ()
Just viewRef -> do
let v = View viewRef
withValidView (View srcRef)
(\srcB srcPat -> do
let newPat = pat `patternApplyOn` srcPat
assignBufferWeakView v srcB newPat
)
(\srcWB srcPat -> do
let newPat = pat `patternApplyOn` srcPat
assignBufferWeakView v srcWB newPat
)
(\srcV srcPat -> do
let newPat = pat `patternApplyOn` srcPat
assignViewWeakView v srcV newPat
)
copyBufferWithPattern :: Buffer mut pin fin heap -> ViewPattern -> IO BufferM
copyBufferWithPattern b pat = do
bsz <- bufferSizeIO b
let !sz = patternSize pat bsz
b' <- newBuffer sz
case pat of
PatternFull -> error "Unreachable code"
Pattern1D poff psz -> copyBuffer b poff b' 0 psz
Pattern2D poff w h stride -> forM_ [0..h-1] $ \r ->
copyBuffer b (poff + r*(w+stride)) b' (r*w) w
PatternOn _p1 _p2 -> forM_ [0..sz-1] $ \off -> do
v <- bufferReadWord8IO b (patternOffset pat off)
bufferWriteWord8IO b' off v
return b'
viewToBuffer :: View -> IO BufferM
viewToBuffer = go PatternFull
where
go :: ViewPattern -> View -> IO BufferM
go pat v = withValidView v
(\b pat2 -> copyBufferWithPattern b (pat `patternApplyOn` pat2))
(\b pat2 -> copyBufferWithPattern b (pat `patternApplyOn` pat2))
(\v2 pat2 -> go (pat `patternApplyOn` pat2) v2)
showViewState :: MonadIO m => View -> m String
showViewState = fmap fst . go
where
go v = withValidView v
(\b pat -> do
sz <- bufferSizeIO b
let psz = patternSize pat sz
return (unlines
[ "View source: buffer"
, "Source size: " ++ show sz
, "View pattern: " ++ show pat
, "Wasted space: " ++ show (100 - ((psz * 100) `div` sz)) ++ "%"
], psz)
)
(\b pat -> do
sz <- bufferSizeIO b
let psz = patternSize pat sz
return (unlines
[ "View source: weak buffer"
, "Source size: " ++ show sz
, "View pattern: " ++ show pat
, "Wasted space: " ++ show (100 - ((psz * 100) `div` sz)) ++ "%"
], psz)
)
(\v2 pat -> do
(r,sz) <- go v2
let psz = patternSize pat sz
return (unlines $
[ "View source: weak view"
, "Source size: " ++ show sz
, "View pattern: " ++ show pat
, "Wasted space: " ++ show (100 - ((psz * 100) `div` sz)) ++ "%"
, "Source:"
] ++ fmap (" " ++) (lines r), psz)
)