packages feed

croaring-ffi-1.0: src/Data/Bitmap/Roaring/FFI/Bitset.hsc

{-# LANGUAGE CApiFFI #-}

{-| Raw bindings to @roaring/bitset/bitset.h@ for CRoaring's growable plain bitset, @bitset_t@.

Each binding is the C function without its @bitset_@ or @bitsets_@ prefix, camelCased.
C creates and frees bitsets.
The handles here carry no ownership.
-}
module Data.Bitmap.Roaring.FFI.Bitset
  ( Bitset (..)
  , array
  , arraySize
  , capacity
  , create
  , createWithCapacity
  , free
  , finalizerFree
  , with
  , copy
  , clear
  , fill
  , resize
  , grow
  , trim
  , sizeInBytes
  , sizeInBits
  , sizeInWords
  , shiftLeft
  , shiftRight
  , set
  , setToValue
  , get
  , count
  , empty
  , minimum
  , maximum
  , inplaceUnion
  , unionCount
  , inplaceIntersection
  , intersectionCount
  , disjoint
  , intersect
  , containsAll
  , inplaceDifference
  , differenceCount
  , inplaceSymmetricDifference
  , symmetricDifferenceCount
  , nextSetBit
  , nextSetBits
  , Callback
  , wrapCallback
  , forEach
  , forEachWith
  , print
  ) where

#include <roaring/bitset/bitset.h>

import Prelude hiding (maximum, minimum, print)

import Data.Bitmap.Roaring.FFI.Callback qualified as Callback
import Data.Bitmap.Roaring.FFI.NullHandle qualified as NullHandle
import Data.Word (Word64)
import Foreign.C.Types (CSize (..))
import Foreign.ForeignPtr (FinalizerPtr)
import Foreign.Ptr (FunPtr, Ptr, nullPtr)
import Foreign.Storable (Storable (..))

-- | @bitset_t@
newtype Bitset = Bitset (Ptr Bitset)
  deriving (Eq, Ord, Show, Storable)

{- | The @array@ field holds the backing buffer of 64-bit words.
Bit @i@ lives in bit @i mod 64@ of word @i div 64@.

The pointer is invalidated by any call that resizes the bitset.
-}
array :: Bitset -> IO (Ptr Word64)
array (Bitset ptr) = #{peek bitset_t, array} ptr
{-# INLINE array #-}

-- | The @arraysize@ field holds the number of words in use.
arraySize :: Bitset -> IO CSize
arraySize (Bitset ptr) = #{peek bitset_t, arraysize} ptr
{-# INLINE arraySize #-}

-- | The @capacity@ field holds the number of words allocated.
capacity :: Bitset -> IO CSize
capacity (Bitset ptr) = #{peek bitset_t, capacity} ptr
{-# INLINE capacity #-}

-- | @bitset_create@: NULL on allocation failure.
foreign import capi unsafe "roaring/bitset/bitset.h bitset_create"
  create :: IO Bitset

-- | @bitset_create_with_capacity@: a bitset of the given number of bits, all zero. NULL on allocation failure.
foreign import capi unsafe "roaring/bitset/bitset.h bitset_create_with_capacity"
  createWithCapacity :: CSize -> IO Bitset

-- | @bitset_free@
foreign import capi unsafe "roaring/bitset/bitset.h bitset_free"
  free :: Bitset -> IO ()

-- | @&bitset_free@
foreign import capi unsafe "roaring/bitset/bitset.h &bitset_free"
  finalizerFree :: FinalizerPtr Bitset

-- | Frees the bitset when the action ends. Throws t'NullHandle.NullHandle' if the constructor returns NULL.
with :: IO Bitset -> (Bitset -> IO a) -> IO a
with = NullHandle.bracket "Data.Bitmap.Roaring.FFI.Bitset.with" free
{-# INLINE with #-}

-- | @bitset_copy@: NULL on allocation failure.
foreign import capi safe "roaring/bitset/bitset.h bitset_copy"
  copy :: Bitset -> IO Bitset

-- | @bitset_clear@: keeps the size.
foreign import capi safe "roaring/bitset/bitset.h bitset_clear"
  clear :: Bitset -> IO ()

-- | @bitset_fill@: sets every bit within the current size.
foreign import capi safe "roaring/bitset/bitset.h bitset_fill"
  fill :: Bitset -> IO ()

{- | @bitset_resize@: resizes to the given number of words.
New words are zeroed when the flag is set.
'False' on allocation failure.
-}
foreign import capi safe "roaring/bitset/bitset.h bitset_resize"
  resize :: Bitset -> CSize -> Bool -> IO Bool

{- | @bitset_grow@: grows to at least the given number of words, zero-padded.
'False' on allocation failure, and when the bitset already has more words.
-}
foreign import capi safe "roaring/bitset/bitset.h bitset_grow"
  grow :: Bitset -> CSize -> IO Bool

-- | @bitset_trim@: 'False' on reallocation failure.
foreign import capi safe "roaring/bitset/bitset.h bitset_trim"
  trim :: Bitset -> IO Bool

-- | @bitset_size_in_bytes@
foreign import capi unsafe "roaring/bitset/bitset.h bitset_size_in_bytes"
  sizeInBytes :: Bitset -> IO CSize

-- | @bitset_size_in_bits@: bits accessible without growing.
foreign import capi unsafe "roaring/bitset/bitset.h bitset_size_in_bits"
  sizeInBits :: Bitset -> IO CSize

-- | @bitset_size_in_words@
foreign import capi unsafe "roaring/bitset/bitset.h bitset_size_in_words"
  sizeInWords :: Bitset -> IO CSize

-- | @bitset_shift_left@: grows the bitset. If growing fails, the bitset silently stays unchanged.
foreign import capi safe "roaring/bitset/bitset.h bitset_shift_left"
  shiftLeft :: Bitset -> CSize -> IO ()

-- | @bitset_shift_right@
foreign import capi safe "roaring/bitset/bitset.h bitset_shift_right"
  shiftRight :: Bitset -> CSize -> IO ()

-- | @bitset_set@: grows the bitset if needed. If growing fails, the bit is silently not set.
foreign import capi unsafe "roaring/bitset/bitset.h bitset_set"
  set :: Bitset -> CSize -> IO ()

-- | @bitset_set_to_value@: grows the bitset if needed. If growing fails, the call silently does nothing.
foreign import capi unsafe "roaring/bitset/bitset.h bitset_set_to_value"
  setToValue :: Bitset -> CSize -> Bool -> IO ()

-- | @bitset_get@: bits beyond the size read as 'False'.
foreign import capi unsafe "roaring/bitset/bitset.h bitset_get"
  get :: Bitset -> CSize -> IO Bool

-- | @bitset_count@
foreign import capi safe "roaring/bitset/bitset.h bitset_count"
  count :: Bitset -> IO CSize

-- | @bitset_empty@
foreign import capi safe "roaring/bitset/bitset.h bitset_empty"
  empty :: Bitset -> IO Bool

-- | @bitset_minimum@: @SIZE_MAX@ when no bit is set.
foreign import capi safe "roaring/bitset/bitset.h bitset_minimum"
  minimum :: Bitset -> IO CSize

-- | @bitset_maximum@: 0 when no bit is set.
foreign import capi safe "roaring/bitset/bitset.h bitset_maximum"
  maximum :: Bitset -> IO CSize

-- | @bitset_inplace_union@: 'False' if growing the first failed. Passing the same bitset twice leaves it unchanged.
inplaceUnion :: Bitset -> Bitset -> IO Bool
inplaceUnion target other
  | target == other = pure True
  | otherwise = inplaceUnionDistinct target other
{-# INLINE inplaceUnion #-}

foreign import capi safe "roaring/bitset/bitset.h bitset_inplace_union"
  inplaceUnionDistinct :: Bitset -> Bitset -> IO Bool

-- | @bitset_union_count@
foreign import capi safe "roaring/bitset/bitset.h bitset_union_count"
  unionCount :: Bitset -> Bitset -> IO CSize

-- | @bitset_inplace_intersection@: passing the same bitset twice leaves it unchanged.
inplaceIntersection :: Bitset -> Bitset -> IO ()
inplaceIntersection target other
  | target == other = pure ()
  | otherwise = inplaceIntersectionDistinct target other
{-# INLINE inplaceIntersection #-}

foreign import capi safe "roaring/bitset/bitset.h bitset_inplace_intersection"
  inplaceIntersectionDistinct :: Bitset -> Bitset -> IO ()

-- | @bitset_intersection_count@
foreign import capi safe "roaring/bitset/bitset.h bitset_intersection_count"
  intersectionCount :: Bitset -> Bitset -> IO CSize

-- | @bitsets_disjoint@
foreign import capi safe "roaring/bitset/bitset.h bitsets_disjoint"
  disjoint :: Bitset -> Bitset -> IO Bool

-- | @bitsets_intersect@
foreign import capi safe "roaring/bitset/bitset.h bitsets_intersect"
  intersect :: Bitset -> Bitset -> IO Bool

-- | @bitset_contains_all@: whether the first has every bit set in the second.
foreign import capi safe "roaring/bitset/bitset.h bitset_contains_all"
  containsAll :: Bitset -> Bitset -> IO Bool

-- | @bitset_inplace_difference@: first minus second. Passing the same bitset twice clears it.
inplaceDifference :: Bitset -> Bitset -> IO ()
inplaceDifference target other
  | target == other = clear target
  | otherwise = inplaceDifferenceDistinct target other
{-# INLINE inplaceDifference #-}

foreign import capi safe "roaring/bitset/bitset.h bitset_inplace_difference"
  inplaceDifferenceDistinct :: Bitset -> Bitset -> IO ()

-- | @bitset_difference_count@: the size of first minus second.
foreign import capi safe "roaring/bitset/bitset.h bitset_difference_count"
  differenceCount :: Bitset -> Bitset -> IO CSize

-- | @bitset_inplace_symmetric_difference@: 'False' if growing the first failed. Passing the same bitset twice clears it.
inplaceSymmetricDifference :: Bitset -> Bitset -> IO Bool
inplaceSymmetricDifference target other
  | target == other = True <$ clear target
  | otherwise = inplaceSymmetricDifferenceDistinct target other
{-# INLINE inplaceSymmetricDifference #-}

foreign import capi safe "roaring/bitset/bitset.h bitset_inplace_symmetric_difference"
  inplaceSymmetricDifferenceDistinct :: Bitset -> Bitset -> IO Bool

-- | @bitset_symmetric_difference_count@
foreign import capi safe "roaring/bitset/bitset.h bitset_symmetric_difference_count"
  symmetricDifferenceCount :: Bitset -> Bitset -> IO CSize

{- | @bitset_next_set_bit@: moves the index behind the pointer to the first set bit at or after it.
'False' when there is none.
The index then stays unchanged.
-}
foreign import capi safe "roaring/bitset/bitset.h bitset_next_set_bit"
  nextSetBit :: Bitset -> Ptr CSize -> IO Bool

{- | @bitset_next_set_bits@: writes the indices of set bits into the buffer, up to the given capacity.
It starts at the index behind the last pointer.
It returns how many indices it wrote, and stores the last index it wrote at that pointer.
To continue, start from one past that index.
-}
foreign import capi safe "roaring/bitset/bitset.h bitset_next_set_bits"
  nextSetBits :: Bitset -> Ptr CSize -> CSize -> Ptr CSize -> IO CSize

-- | @bitset_iterator@: returning 'False' stops the iteration.
type Callback = CSize -> Ptr () -> IO Bool

-- | Release the result with 'Foreign.Ptr.freeHaskellFunPtr'.
foreign import ccall "wrapper"
  wrapCallback :: Callback -> IO (FunPtr Callback)

-- | @bitset_for_each@: 'True' if the callback never stopped the iteration.
foreign import capi safe "roaring/bitset/bitset.h bitset_for_each"
  forEach :: Bitset -> FunPtr Callback -> Ptr () -> IO Bool

{- | Runs 'forEach' with a Haskell function.
'True' if the function never returned 'False'.

An exception from the function stops the iteration and is rethrown after it, instead of unwinding through C.
-}
forEachWith :: Bitset -> (CSize -> IO Bool) -> IO Bool
forEachWith bitset visit = Callback.with wrapCallback visit $ \callback -> forEach bitset callback nullPtr

-- | @bitset_print@: writes to C's @stdout@.
foreign import capi safe "roaring/bitset/bitset.h bitset_print"
  print :: Bitset -> IO ()