{-# LANGUAGE MagicHash #-}

module Data.Unique
  ( Unique,
    newUnique,
    hashUnique,
  )
where

import Data.IORef (IORef, atomicModifyIORef', newIORef)
import GHC.Int (Int (..))
import GHC.Internal.Integer (Integer, integerToInt#)
import System.IO.Unsafe (unsafePerformIO)
import Prelude (Bool (..), Eq (..), IO, Ord (..), (+))

-- | An abstract value generated by 'newUnique'.
newtype Unique = Unique Integer

instance Eq Unique where
  Unique left == Unique right = left == right
  Unique left /= Unique right = left /= right

instance Ord Unique where
  compare (Unique left) (Unique right) = compare left right
  Unique left < Unique right = left < right
  Unique left <= Unique right = left <= right
  Unique left > Unique right = left > right
  Unique left >= Unique right = left >= right
  max left right =
    case left >= right of
      True -> left
      False -> right
  min left right =
    case left <= right of
      True -> left
      False -> right

uniqueSource :: IORef Integer
uniqueSource = unsafePerformIO (newIORef 0)
{-# NOINLINE uniqueSource #-}

-- | Create a value distinct from every value returned by an earlier call.
newUnique :: IO Unique
newUnique = atomicModifyIORef' uniqueSource advanceUnique

advanceUnique :: Integer -> (Integer, Unique)
advanceUnique current =
  let next = current + 1
   in (next, Unique next)

-- | Convert a unique value to a suitable 'Int' hash key.
hashUnique :: Unique -> Int
hashUnique (Unique value) = I# (integerToInt# value)
