Skip to content

Instantly share code, notes, and snippets.

@matthewcrews
Created August 8, 2022 16:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewcrews/399889e6cdb35fb42b40815e200d777c to your computer and use it in GitHub Desktop.
Save matthewcrews/399889e6cdb35fb42b40815e200d777c to your computer and use it in GitHub Desktop.
open System
open System.Collections
open System.Collections.Generic
module private Helpers =
let inline computeBucketAndMask (itemKey: int<'Item>) itemCount =
if (uint itemKey) >= (uint itemCount) then
raise (IndexOutOfRangeException (nameof itemKey))
let location = int itemKey
let bucket = location >>> 6
let offset = location &&& 0b111111
let mask = 1UL <<< offset
bucket, mask
type BitSet<[<Measure>] 'Measure> (itemCount: int, buckets: uint64[]) =
new (itemCount: int) =
let bucketsRequired = (itemCount + 63) >>> 6
let buckets : uint64[] = Array.zeroCreate bucketsRequired
BitSet<_> (itemCount, buckets)
// These need to be public to support inlining
member _.ItemCount = itemCount
member _._buckets = buckets
member _.Capacity = buckets.Length * 64
member _.Values : ReadOnlySpan<uint64> = ReadOnlySpan buckets
member inline b.Count =
let mutable total = 0
for bucket in b._buckets do
total <- total + System.Numerics.BitOperations.PopCount bucket
total
member b.Item
with inline get (itemKey: int<'Measure>) =
let bucketId, mask = Helpers.computeBucketAndMask itemKey b.ItemCount
let buckets = b._buckets
let bucket = buckets[bucketId]
(bucket &&& mask) <> 0UL
member b.Clear () =
for i = 0 to b._buckets.Length - 1 do
b._buckets[i] <- 0UL
member b.Contains (itemKey: int<'Measure>) =
let bucketId, mask = Helpers.computeBucketAndMask itemKey b.ItemCount
let buckets = b._buckets
let bucket = buckets[bucketId]
(bucket &&& mask) <> 0UL
member inline b.Add (itemKey: int<'Measure>) =
let bucketId, mask = Helpers.computeBucketAndMask itemKey b.ItemCount
let buckets = b._buckets
let bucket = buckets[bucketId]
buckets[bucketId] <- bucket ||| mask
member inline b.Remove (itemKey: int<'Measure>) =
let bucketId, mask = Helpers.computeBucketAndMask itemKey b.ItemCount
let buckets = b._buckets
let bucket = buckets[bucketId]
buckets[bucketId] <- bucket &&& ~~~mask
static member create (count: int<'Measure>) =
BitSet<'Measure> (int count)
static member inline iter ([<InlineIfLambda>] f: int<'Measure> -> unit) (b: BitSet<'Measure>) =
let mutable i = 0
// Source of algorithm: https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
while i < b._buckets.Length do
let mutable bitSet = b._buckets[i]
while bitSet <> 0UL do
let r = System.Numerics.BitOperations.TrailingZeroCount bitSet
let itemId =
(i <<< 6) + r
|> LanguagePrimitives.Int32WithMeasure<'Measure>
(f itemId)
bitSet <- bitSet ^^^ (1UL <<< r)
i <- i + 1
[<Measure>] type Chicken
let test (b: BitSet<Chicken>) (myByRef: int<Chicken> byref) =
b
|> BitSet.iter (fun a -> myByRef <- myByRef + a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment