{- |
In principle you can traverse through a storable vector
using repeated calls to @viewL@ or using @index@.
However this needs a bit of pointer arrangement and allocation.
This data structure should make loops optimally fast.
-}
module Data.StorableVector.Pointer (
   Pointer(..),
   cons,
   viewL,
   switchL,
   ) where

-- import qualified Data.StorableVector as V
import qualified Data.StorableVector.Base as VB

import qualified Foreign.ForeignPtr as FPtr
import Foreign.Marshal.Array (advancePtr, )
import Foreign.Storable (Storable, peek, )
import Foreign (Ptr, )
import qualified System.Unsafe as Unsafe


{-
The reference to the ForeignPtr asserts,
that the array is maintained and thus is not garbage collected.
The Ptr we use for traversing would not achieve this.
-}
{- |
We might have name the data type iterator.
-}
data Pointer a =
   Pointer {
      forall a. Pointer a -> ForeignPtr a
fptr :: {-# UNPACK #-} !(FPtr.ForeignPtr a),
      forall a. Pointer a -> Ptr a
ptr  :: {-# UNPACK #-} !(Ptr a),
      forall a. Pointer a -> Int
left :: {-# UNPACK #-} !Int
   }


{-# INLINE cons #-}
cons :: Storable a => VB.Vector a -> Pointer a
cons :: forall a. Storable a => Vector a -> Pointer a
cons (VB.SV ForeignPtr a
fp Int
s Int
l) =
   forall a. ForeignPtr a -> Ptr a -> Int -> Pointer a
Pointer ForeignPtr a
fp (forall a. Storable a => Ptr a -> Int -> Ptr a
advancePtr (forall a. ForeignPtr a -> Ptr a
Unsafe.foreignPtrToPtr ForeignPtr a
fp) Int
s) Int
l


{-# INLINE viewL #-}
viewL :: Storable a => Pointer a -> Maybe (a, Pointer a)
viewL :: forall a. Storable a => Pointer a -> Maybe (a, Pointer a)
viewL = forall a b.
Storable a =>
b -> (a -> Pointer a -> b) -> Pointer a -> b
switchL forall a. Maybe a
Nothing (forall a b c. ((a, b) -> c) -> a -> b -> c
curry forall a. a -> Maybe a
Just)

{-# INLINE switchL #-}
switchL :: Storable a =>
   b -> (a -> Pointer a -> b) -> Pointer a -> b
switchL :: forall a b.
Storable a =>
b -> (a -> Pointer a -> b) -> Pointer a -> b
switchL b
n a -> Pointer a -> b
j (Pointer ForeignPtr a
fp Ptr a
p Int
l) =
   if Int
lforall a. Ord a => a -> a -> Bool
<=Int
0
     then b
n
     else a -> Pointer a -> b
j (forall a. IO a -> a
VB.inlinePerformIO (forall a. Storable a => Ptr a -> IO a
peek Ptr a
p)) (forall a. ForeignPtr a -> Ptr a -> Int -> Pointer a
Pointer ForeignPtr a
fp (forall a. Storable a => Ptr a -> Int -> Ptr a
advancePtr Ptr a
p Int
1) (Int
lforall a. Num a => a -> a -> a
-Int
1))
-- Unsafe.performIO at this place would make SpeedPointer test 0.5 s slower