module Data.CompactMap.Buffer where
import Foreign (Ptr,Storable(..),plusPtr, castPtr)
import Data.CompactMap.MemoryMap
import Data.CompactMap.Types
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr, castForeignPtr)
import Foreign.Concurrent
import Data.IORef
newBuffer :: Int -> IO Buffer
newBuffer initSize
= do aligned <- alignSize initSize
dataPtr <- mmap aligned [Read,Write] [Anonymous,Private,NoReserve]
fptr <- newIORef =<< newForeignPtr dataPtr (munmap dataPtr initSize)
old <- newIORef []
posRef <- newFastMutInt 0
size <- newFastMutInt aligned
return $ Buffer{ bufferData = fptr
, bufferOld = old
, bufferPos = posRef
, bufferSize = size }
withBytes :: Buffer -> Int -> (Ptr a -> IO b) -> IO b
withBytes !Buffer{bufferPos=bufferPos,bufferData=bufferData,bufferSize=bufferSize,bufferOld=bufferOld} !bytesNeeded fn
= do !currentPos <- readFastMutInt bufferPos
!currentSize <- readFastMutInt bufferSize
!oldPtr <- readIORef bufferData
if currentSize >= currentPos + bytesNeeded
then do writeFastMutInt bufferPos (currentPos+bytesNeeded)
withForeignPtr (castForeignPtr oldPtr) $ \ptr -> fn $! (ptr `plusPtr` currentPos)
else do let minSize = max bytesNeeded currentSize
newSize = minSize + minSize `div` 4
aligned <- alignSize newSize
!newPtr <- mmap aligned [Read,Write] [Anonymous,Private,NoReserve]
fptr <- newForeignPtr newPtr (munmap newPtr aligned)
writeIORef bufferData fptr
modifyIORef bufferOld (oldPtr:)
writeFastMutInt bufferPos bytesNeeded
writeFastMutInt bufferSize aligned
fn $! (castPtr newPtr)
touchBuffer :: Buffer -> IO ()
touchBuffer buffer
= do touchForeignPtr =<< readIORef (bufferData buffer)
ls <- readIORef (bufferOld buffer)
mapM_ touchForeignPtr ls