packages feed

radix-tree-1.1.0.0: src/Data/RadixNTree/Word8/Strict/Pointer.hs

{-# LANGUAGE BangPatterns
           , DeriveLift
           , RankNTypes
           , ScopedTypeVariables #-}

module Data.RadixNTree.Word8.Strict.Pointer
  ( Pointer (..)
  , pointer0
  , pointer1

  , follow0
  , follow1
  ) where

import           Data.ByteArray.NonEmpty
import           Data.RadixNTree.Word8.Key
import           Data.RadixNTree.Word8.Strict

import           Control.Monad.ST
import           Data.Bits
import           Data.Primitive.ByteArray
import           Data.Word
import           Language.Haskell.TH.Syntax



-- | Pure compressed tree reference.
--
--   @since 1.1
data Pointer = Pointer
                 {-# UNPACK #-} !Word -- ^ Node depth (0 is root).
                 !ByteArray           -- ^ Little-endian bitmask of size @depth@.
                                      --   'Bin' choices are represented as 0 and 1 for
                                      --   left and right respectively;
                                      --   'Tip's can hold any data.
               deriving (Show, Lift)



-- | Mark a bit at the given depth as @1@.
mark :: MutableByteArray s -> Word -> ST s ()
mark marr n = do
  let x = fromIntegral $ n `unsafeShiftR` 3

      y = fromIntegral $ n .&. 0x07

  i <- readByteArray marr x

  let i' = i .|. unsafeShiftL 1 y

  writeByteArray marr x (i' :: Word8)

-- | Check if the bit at the given depth is @0@.
left :: ByteArray -> Word -> Bool
left arr n =
  let x = fromIntegral $ n `unsafeShiftR` 3

      y = fromIntegral $ n .&. 0x07

  in (unsafeShiftR (indexByteArray arr x :: Word8) y) .&. 0x1 == 0

-- | Create a bitmask that can hold @depth@ bits and populate it.
form :: (forall s. MutableByteArray s -> ST s ()) -> Word -> ByteArray
form go n = do
  runST $ do
    let m = fromIntegral (n `unsafeShiftR` 3) + 1
    marr <- newByteArray m
    fillByteArray marr 0 m 0x00
    go marr
    unsafeFreezeByteArray marr



{-# INLINE pointer0 #-}
pointer0 :: Feed -> RadixTree a -> Maybe Pointer
pointer0 (Feed feed) = \(RadixTree mx t) ->
  feed $ \step s ->
    case step s of
      More w z -> pointer_ step w z t
      Done     ->
        case mx of
          Just _  -> Just $ Pointer 0 emptyByteArray
          Nothing -> Nothing

{-# INLINE pointer1 #-}
pointer1 :: Feed1 -> Radix1Tree a -> Maybe Pointer
pointer1 (Feed1 w feed) = feed $ \step -> pointer_ step w

{-# INLINE pointer_ #-}
pointer_
  :: (x -> Step Word8 x)
  -> Word8 -> x -> Radix1Tree a -> Maybe Pointer
pointer_ (step :: x -> Step Word8 x) = go 0 (\_ -> pure ())
  where
    go :: Word -> (forall s. MutableByteArray s -> ST s ())
       -> Word8 -> x -> Radix1Tree a -> Maybe Pointer
    go !i acc !w !s t =
      case t of
        Bin p l r ->
          if w < p
            then go (i + 1)                          acc       w s l
            else go (i + 1) (\marr -> mark marr i >> acc marr) w s r

        Tip arr mx dx -> goarr w s 0
          where
            goarr v !z n
              | v == indexByteArray arr n =
                  let n' = n + 1
                  in if n' >= sizeofByteArray arr
                       then case step z of
                              More u z' -> go (i + 1) acc u z' dx
                              Done      ->
                                case mx of
                                  Just _  -> Just $ Pointer (i + 1) (form acc i)
                                  Nothing -> Nothing

                       else case step z of
                              More u z' -> goarr u z' n'
                              Done      -> Nothing

              | otherwise = Nothing

        Nil -> Nothing



follow0 :: a -> Pointer -> RadixTree a -> a
follow0 d (Pointer len arr) (RadixTree mx dx)
  | len == 0  = case mx of
                  Just x  -> x
                  Nothing -> d

  | otherwise = follow_ d len arr dx

follow1 :: a -> Pointer -> Radix1Tree a -> a
follow1 d (Pointer len arr) = follow_ d len arr

follow_ :: a -> Word -> ByteArray -> Radix1Tree a -> a
follow_ d len arr = go 0
  where
    go !i t =
      case t of
        Bin _ l r ->
          go (i + 1) $ if left arr i
                         then l
                         else r

        Tip _ mx dx ->
          let i' = i + 1
          in if i' > len
               then d
               else if i' == len
                      then case mx of
                             Just x  -> x
                             Nothing -> d

                      else go i' dx

        Nil -> d