module Web.Cuid.Internal (
formatNumber, formatPadded, formatShort,
getNextCount, getRandomValue, getTimestamp, myFingerprint
) where
import Control.Monad (liftM)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Char (ord)
import Data.IORef (IORef, newIORef, atomicModifyIORef')
import Data.Text (Text, append)
import Data.Time.Clock.POSIX (getPOSIXTime)
import Network.HostName (getHostName)
import System.IO.Unsafe (unsafePerformIO)
import System.Random.MWC (GenIO)
import qualified System.Random.MWC as MWC
import Web.Cuid.Internal.Formatting
#if defined(mingw32_HOST_OS)
import System.Win32 (ProcessId, failIfZero)
#else
import System.Posix.Process (getProcessID)
#endif
maxCount :: Int
maxCount = formatBase ^ blockSize
getFingerprint :: IO (Int, Int)
getFingerprint = do
pid <- getPid
hostname <- getHostName
let hostSum = 36 + length hostname + sum (map ord hostname)
return (pid, hostSum)
myFingerprint :: Text
myFingerprint = unsafePerformIO $ do
(pid, host) <- getFingerprint
return (formatShort pid `append` formatShort host)
generator :: GenIO
generator = unsafePerformIO MWC.create
getRandomValue :: IO Int
getRandomValue = MWC.uniformR (0, maxCount) generator
counter :: IORef Int
counter = unsafePerformIO (newIORef 0)
getNextCount :: IO Int
getNextCount = postIncrement counter
postIncrement :: MonadIO m => IORef Int -> m Int
postIncrement c = liftIO (atomicModifyIORef' c incrementAndWrap) where
incrementAndWrap count = (succ count `mod` maxCount, count)
getTimestamp :: IO Int
getTimestamp = liftM toMillis getPOSIXTime where
toMillis posix = round (posix * 1000)
getPid :: MonadIO m => m Int
#if defined(mingw32_HOST_OS)
foreign import stdcall unsafe "windows.h GetCurrentProcessId"
c_GetCurrentProcessId :: IO ProcessId
getCurrentProcessId :: IO ProcessId
getCurrentProcessId = failIfZero "GetCurrentProcessId" c_GetCurrentProcessId
getPid = liftM fromIntegral (liftIO getCurrentProcessId)
#else
getPid = liftM fromIntegral (liftIO getProcessID)
#endif