{-# language KindSignatures #-} {-# language BangPatterns #-} {-# language RoleAnnotations #-} {-# language MagicHash #-} {-# language UnboxedTuples #-} {-# language RankNTypes #-} {-# language DeriveTraversable #-} {-# language Unsafe #-} {- OPTIONS_GHC -ddump-simpl #-} module Data.CompactSequence.Internal.Array where import Data.CompactSequence.Internal.Size import Data.Primitive.SmallArray import Control.Monad.ST.Strict import GHC.Exts (SmallArray#) newtype Array n a = Array (SmallArray a) deriving (Functor, Foldable, Traversable) type role Array nominal representational singleton :: a -> Array Sz1 a singleton x = Array (pure x) -- | Unsafely convert a 'SmallArray' of size @n@ -- to an @'Array' n@. This is genuinely unsafe: if -- @n@ is greater than the true array size, then -- some operation will eventually violate memory safety. unsafeSmallArrayToArray :: SmallArray a -> Array n a unsafeSmallArrayToArray = Array arrayToSmallArray :: Array n a -> SmallArray a arrayToSmallArray (Array sa) = sa getSingleton# :: Array Sz1 a -> (# a #) getSingleton# (Array sa) = indexSmallArray## sa 0 getSingletonA :: Applicative f => Array Sz1 a -> f a getSingletonA (Array sa) | (# a #) <- indexSmallArray## sa 0 = pure a splitArray :: Size n -> Array (Twice n) a -> (Array n a, Array n a) splitArray (Size len) (Array sa) | (# sa1, sa2 #) <- splitSmallArray# len sa = (Array (SmallArray sa1), Array (SmallArray sa2)) {-# INLINE splitArray #-} -- Bleh. We use this gunk to prevent coercions from getting -- in the way of worker/wrapper, and also to deal with the -- nested CPR challenge. GHC, please fix yourself. -- We want everything unboxed, but it seems unlikely that we'll -- win significantly by inlining two calls to an out-of-line -- primop. The giant mutually recursive group of 8 functions -- that implement the basic deque operations needs to be as -- small as we can possibly make it if there's to be any hope -- for the instruction cache. splitSmallArray# :: Int -> SmallArray a -> (# SmallArray# a, SmallArray# a #) splitSmallArray# len sa1 = (# sa2, sa3 #) where !(SmallArray sa2) = cloneSmallArray sa1 0 len !(SmallArray sa3) = cloneSmallArray sa1 len len {-# NOINLINE splitSmallArray# #-} -- | Append two arrays of the same size. We take the size -- of the argument arrays so we can build the result array -- before loading the first argument array into cache. Is -- this the right approach? Not sure. We *certainly* don't -- want to just use `<>`, because append :: Size n -> Array n a -> Array n a -> Array (Twice n) a append (Size n) (Array xs) (Array ys) = Array $ appendSmallArrays n xs ys -- WAT. For some reason, if I put the actual machinery of this in 'append' and -- say NOINLINE, GHC (8.6.3 and 8.8.1 at least) doesn't perform worker-wrapper! -- Ugh. appendSmallArrays :: Int -> SmallArray a -> SmallArray a -> SmallArray a appendSmallArrays n xs ys = createSmallArray (2*n) (error "Data.CompactSequence.Internal.Array.append: Internal error") $ \sma -> copySmallArray sma 0 xs 0 n *> copySmallArray sma n ys 0 n -- Small though this is, I don't really see much point in inlining it; it calls -- several out-of-line primops that aren't super-cheap anyway. I'd rather cut -- code size. This will change completely, of course, once GHC gets a primop -- for appending arrays. {-# NOINLINE appendSmallArrays #-} -- Shamelessly stolen from primitive. createSmallArray :: Int -> a -> (forall s. SmallMutableArray s a -> ST s ()) -> SmallArray a createSmallArray n x f = runSmallArray $ do mary <- newSmallArray n x f mary pure mary arraySplitListN :: Size n -> [a] -> (Array n a, [a]) arraySplitListN (Size n) xs | (sa, xs') <- smallArraySplitListN n xs = (Array sa, xs') smallArraySplitListN :: Int -> [a] -> (SmallArray a, [a]) smallArraySplitListN n l = runST $ do sma <- newSmallArray n (error "smallArraySplitListN: uninitialized") let go !ix [] = if ix == n then do sa <- unsafeFreezeSmallArray sma pure (sa, []) else error "smallArraySplitListN: list length less than specified size" go !ix xss@(x : xs) = if ix < n then do writeSmallArray sma ix x go (ix+1) xs else do sa <- unsafeFreezeSmallArray sma pure (sa, xss) go 0 l fromList :: Size n -> [a] -> Array n a fromList (Size n) xs = Array (smallArrayFromListN n xs)