module Data.CompactMap.Buffer
( newBuffer
, withBytes
, touchBuffer
) where
import Data.CompactMap.Types
import Foreign (Ptr, plusPtr, castPtr)
import Foreign.ForeignPtr (withForeignPtr, touchForeignPtr, castForeignPtr)
import Data.IORef
import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
newBuffer :: Int -> IO Buffer
newBuffer initSize
= do fptr <- newIORef =<< mallocPlainForeignPtrBytes initSize
old <- newIORef []
posRef <- newFastMutInt 0
size <- newFastMutInt initSize
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
fptr <- mallocPlainForeignPtrBytes newSize
writeIORef bufferData fptr
modifyIORef bufferOld (oldPtr:)
writeFastMutInt bufferPos bytesNeeded
writeFastMutInt bufferSize newSize
withForeignPtr fptr $ fn . castPtr
touchBuffer :: Buffer -> IO ()
touchBuffer buffer
= do touchForeignPtr =<< readIORef (bufferData buffer)
ls <- readIORef (bufferOld buffer)
mapM_ touchForeignPtr ls