packages feed

hsc3-server-0.3.0: Sound/SC3/Server/Allocator/SetAllocator.hs

{-# LANGUAGE BangPatterns
           , FlexibleContexts
           , TypeFamilies #-}
module Sound.SC3.Server.Allocator.SetAllocator (
    SetAllocator
  , cons
) where

import Control.Failure (Failure, failure)
import Control.DeepSeq (NFData(..))
import qualified Data.BitSet as Set
import Sound.SC3.Server.Allocator

data SetAllocator i =
    SetAllocator
        {-# UNPACK #-} !(Range i)
        {-# UNPACK #-} !(Set.BitSet i)
                       !i
        deriving (Eq, Show)

instance NFData i => NFData (SetAllocator i) where
    rnf (SetAllocator x1 x2 x3) =
        rnf x1 `seq`
            x2 `seq`
        rnf x3 `seq` ()

cons :: Range i -> SetAllocator i
cons r = SetAllocator r Set.empty (begin r)

-- | Convert an id to a bit index.
--
-- This is necessary to keep the BitSet size bounded between [0, numIds[.
toBit :: Integral i => Range i -> i -> i
toBit r i = i - begin r

findNext :: (Integral i) => SetAllocator i -> Maybe i
findNext (SetAllocator r u i)
    | fromIntegral (size r) == Set.size u = Nothing
    | otherwise = loop i
    where
        wrap i = if i >= end r
                    then begin r
                    else i
        loop !i = let i' = wrap (i+1)
                  in if Set.member (toBit r i') u
                     then loop i'
                     else Just i'

_alloc :: (Integral i, Failure AllocFailure m) => SetAllocator i -> m (i, SetAllocator i)
_alloc a@(SetAllocator r u i) =
    case findNext a of
        Nothing -> failure NoFreeIds
        Just i' -> return (i, SetAllocator r (Set.insert (toBit r i) u) i')

_free :: (Integral i, Failure AllocFailure m) => i -> SetAllocator i -> m (SetAllocator i)
_free i (SetAllocator r u n) =
    if Set.member (toBit r i) u
    then let u' = Set.delete (toBit r i) u
         in return (SetAllocator r u' n)
    else failure InvalidId

_statistics :: (Integral i) => SetAllocator i -> Statistics
_statistics (SetAllocator r u _) =
    let k = fromIntegral (size r)
        n = Set.size u
    in Statistics {
        numAvailable = k
      , numFree = k - n
      , numUsed = n }

instance (Integral i) => IdAllocator (SetAllocator i) where
    type Id (SetAllocator i) = i
    alloc                    = _alloc
    free                     = _free
    statistics               = _statistics