parameterized-utils-2.1.4.0: src/Data/Parameterized/Nonce/Unsafe.hs
------------------------------------------------------------------------
-- |
-- Module : Data.Parameterized.Nonce.Unsafe
-- Description : A counter in the ST monad.
-- Copyright : (c) Galois, Inc 2014
-- Maintainer : Joe Hendrix <jhendrix@galois.com>
-- Stability : provisional
--
-- This module provides a simple generator of new indexes in the ST monad.
-- It is predictable and not intended for cryptographic purposes.
--
-- NOTE: the 'TestEquality' and 'OrdF' instances for the 'Nonce' type simply
-- compare the generated nonce values and then assert to the compiler
-- (via 'unsafeCoerce') that the types ascribed to the nonces are equal
-- if their values are equal. This is only OK because of the discipline
-- by which nonces should be used: they should only be generated from
-- a 'NonceGenerator' (i.e., should not be built directly), and nonces from
-- different generators must never be compared! Arranging to compare
-- Nonces from different origins would allow users to build 'unsafeCoerce'
-- via the 'testEquality' function.
--
-- This module is deprecated, and should not be used in new code.
-- Clients of this module should migrate to use "Data.Parameterized.Nonce".
-------------------------------------------------------------------------
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE Unsafe #-}
module Data.Parameterized.Nonce.Unsafe
{-# DEPRECATED "Migrate to use Data.Parameterized.Nonce instead, this module will be removed soon." #-}
( NonceGenerator
, newNonceGenerator
, freshNonce
, atLimit
, Nonce
, indexValue
) where
import Control.Monad.ST
import Data.Hashable
import Data.STRef
import Data.Word
import Unsafe.Coerce
import Data.Parameterized.Axiom
import Data.Parameterized.Classes
-- | A simple type that for getting fresh indices in the 'ST' monad.
-- The type parameter @s@ is used for the 'ST' monad parameter.
newtype NonceGenerator s = NonceGenerator (STRef s Word64)
-- | Create a new counter.
newNonceGenerator :: ST s (NonceGenerator s)
newNonceGenerator = NonceGenerator `fmap` newSTRef (toEnum 0)
-- | An index generated by the counter.
newtype Nonce (tp :: k) = Nonce { indexValue :: Word64 }
deriving (Eq, Ord, Hashable, Show)
-- Force the type role of Nonce to be nominal: this prevents Data.Coerce.coerce
-- from casting the types of nonces, which it would otherwise be able to do
-- because tp is a phantom type parameter. This partially helps to protect
-- the nonce abstraction.
type role Nonce nominal
instance TestEquality Nonce where
testEquality x y | indexValue x == indexValue y = Just unsafeAxiom
| otherwise = Nothing
instance OrdF Nonce where
compareF x y =
case compare (indexValue x) (indexValue y) of
LT -> LTF
EQ -> unsafeCoerce EQF
GT -> GTF
instance HashableF Nonce where
hashWithSaltF s (Nonce x) = hashWithSalt s x
instance ShowF Nonce
{-# INLINE freshNonce #-}
-- | Get a fresh index and increment the counter.
freshNonce :: NonceGenerator s -> ST s (Nonce tp)
freshNonce (NonceGenerator r) = do
i <- readSTRef r
writeSTRef r $! succ i
return (Nonce i)
-- | Return true if counter has reached the limit, and can't be
-- incremented without risk of error.
atLimit :: NonceGenerator s -> ST s Bool
atLimit (NonceGenerator r) = do
i <- readSTRef r
return (i == maxBound)