{"id":26541576,"url":"https://github.com/peter-juhasz/value-bit-array","last_synced_at":"2025-10-24T01:48:36.906Z","repository":{"id":89618080,"uuid":"502436290","full_name":"Peter-Juhasz/value-bit-array","owner":"Peter-Juhasz","description":"Allocation free BitArray implementation","archived":false,"fork":false,"pushed_at":"2022-06-11T21:06:18.000Z","size":12,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-26T22:17:54.153Z","etag":null,"topics":["bits","csharp","dotnet","performance","zero-allocation"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Peter-Juhasz.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-06-11T19:16:55.000Z","updated_at":"2024-12-23T02:22:05.000Z","dependencies_parsed_at":null,"dependency_job_id":"cf0a4976-5b82-4bae-9a15-edcf2c995a8d","html_url":"https://github.com/Peter-Juhasz/value-bit-array","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Peter-Juhasz/value-bit-array","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Peter-Juhasz%2Fvalue-bit-array","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Peter-Juhasz%2Fvalue-bit-array/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Peter-Juhasz%2Fvalue-bit-array/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Peter-Juhasz%2Fvalue-bit-array/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Peter-Juhasz","download_url":"https://codeload.github.com/Peter-Juhasz/value-bit-array/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Peter-Juhasz%2Fvalue-bit-array/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279018724,"owners_count":26086612,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bits","csharp","dotnet","performance","zero-allocation"],"created_at":"2025-03-22T01:39:13.556Z","updated_at":"2025-10-14T13:41:40.997Z","avatar_url":"https://github.com/Peter-Juhasz.png","language":"C#","readme":"# Allocation free BitArray implementation\r\n\r\n## Introduction\r\nThe built-in type [BitArray](https://docs.microsoft.com/en-us/dotnet/api/system.collections.bitarray) is a very old type, which exists in the framework since [.NET Framework 1.1](https://docs.microsoft.com/en-us/dotnet/api/system.collections.bitarray?view=netframework-1.1#applies-to). It is a general use collection, implemented as a reference type.\r\n\r\nWhile the built-in type [BitVector32](https://docs.microsoft.com/en-us/dotnet/api/system.collections.specialized.bitvector32) is a value type, its size is limited to 32 bits.\r\n\r\nThe following implementation uses zero heap allocations, so it is suitable for high performance scenarios, but only where the number of elements in the bit array is small (or fits the the stack).\r\n\r\n## Implementation\r\nWe are going to store bits batched as unsigned integers (instead of array of booleans, because a boolean has the size of a byte) to save memory (similarly to the built-in type [BitArray](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Collections/src/System/Collections/BitArray.cs)). The largest integer type which supports native bit operations on most platforms is `ulong`, so we are going to use that as a storage primitive (we could use `nuint` as well for a platform independent native integer).\r\n\r\nOur storage would look like this:\r\n```\r\n0000000000000000000000000000000000000000000000000000000000000000 000000000...\r\n---------------------------------------------------------------- ---------...\r\n                    sizeof(ulong) = 64 bits\r\n                          buffer[0]                              buffer[1]...\r\n```\r\n\r\nA backing buffer is still needed, but with the promise of no heap allocations we must do that on the stack. And as long as we have only a relatively small number of bits (easily can be even thousands), we can allocate the buffer on the stack:\r\n```cs\r\nSpan\u003culong\u003e buffer = stackalloc ulong[4]; // can store 4 * 64 bits = 256 bits\r\n```\r\n\r\nLet's wrap it into our value type `ValueBitArray` to use that buffer for its intended purpose:\r\n```cs\r\npublic ref struct ValueBitArray\r\n{\r\n\tpublic ValueBitArray(Span\u003culong\u003e buffer)\r\n\t{\r\n\t\t_buffer = buffer;\r\n\t}\r\n\t\r\n\tprivate readonly Span\u003culong\u003e _buffer;\r\n}\r\n```\r\n\r\n*Note that the type must be a `ref struct` because it references a `Span\u003culong\u003e`.*\r\n\r\nNow we need to calculate where is a bit with a specific index. For example bit 67 is in the second (with index 0) bucket, at forth (index 3) position:\r\n```\r\n0000000000000000000000000000000000000000000000000000000000000000 0000000000...\r\n---------------------------------------------------------------- ----------...\r\n                    sizeof(ulong) = 64 bits                         ^ index 67\r\n\t\t          0-63 index                             64-127 index  \r\n```\r\n\r\nTo do that, we can use simple integer math:\r\n```cs\r\nprivate const int NumberOfBitsInBucket = sizeof(ulong) * 8;\r\n\r\npublic bool this[int index]\r\n{\r\n\tget\r\n\t{\r\n\t\tint bucket = index / NumberOfBitsInBucket;\r\n\t\tint position = index % NumberOfBitsInBucket;\r\n\t\t// ...\r\n\t}\r\n}\r\n```\r\n\r\nIt takes two operations, but it can be done at once using [DivRem](https://docs.microsoft.com/en-us/dotnet/api/system.math.divrem?#system-math-divrem(system-uint64-system-uint64)):\r\n```cs\r\n(int bucket, int position) = Math.DivRem(index, NumberOfBitsInBucket);\r\n```\r\n\r\nWe can decide whether a specific bit at a specified position is turned on using the following formula:\r\n```cs\r\ninteger \u0026 (1 \u003c\u003c position) \u003e 0\r\n```\r\n\r\nAnd at this point we have everything to implement reads:\r\n```cs\r\npublic bool this[int index]\r\n{\r\n\tget\r\n\t{\r\n\t\t(int bucket, int position) = Math.DivRem(index, NumberOfBitsInBucket);\r\n\t\treturn (_buffer[bucket] \u0026 (1ul \u003c\u003c position)) \u003e 0ul;\r\n\t}\r\n}\r\n```\r\n\r\nWith the following formulas we can set a bit of an integer at a specific position to either `true`:\r\n```cs\r\ninteger = integer | (1 \u003c\u003c position);\r\n```\r\n\r\nor `false`:\r\n```cs\r\ninteger = integer \u0026 ~(1 \u003c\u003c position);\r\n```\r\n\r\nSo we can easily implement writes:\r\n```cs\r\npublic bool this[int index]\r\n{\r\n\tset\r\n\t{\r\n\t\t(int bucket, int position) = Math.DivRem(index, NumberOfBitsInBucket);\r\n\t\t_buffer[bucket] = value switch\r\n\t\t{\r\n\t\t\ttrue =\u003e _buffer[bucket] | (1ul \u003c\u003c position),\r\n\t\t\tfalse =\u003e _buffer[bucket] \u0026 ~(1ul \u003c\u003c position),\r\n\t\t};\r\n\t}\r\n}\r\n```\r\n\r\nAnd we are done.\r\n\r\n## Usage\r\nTo use our new bit array type, first we need to pre-allocate the buffer (on stack), and then we can easily set bits:\r\n```cs\r\nSpan\u003culong\u003e buffer = stackalloc ulong[8]; // 8 * 64 bits = 512 bits\r\nvar array = new ValueBitArray(buffer);\r\n\r\n// all bits are turned off by default\r\nvar firstIsOff = array[0]; \r\n\r\narray[0] = true; // set first bit to true\r\nvar firstIsOn = array[0]; // true, read first bit\r\n\r\narray[0] = false; // set first bit back to false\r\n\r\narray[511] = true; // set very last bit to true\r\n\r\narray[512]; // out of range, ArgumentOutOfRangeException\r\n```\r\n\r\n## Appendix\r\n\r\n### Count\r\nTotal count of bits which can be stored in the buffer, can be easily calculated:\r\n```cs\r\npublic int Count =\u003e _buffer.Length * NumberOfBitsInBucket;\r\n```\r\n\r\n### Reset\r\nResetting all bits to zero can be easily implemented using the [Fill](https://docs.microsoft.com/en-us/dotnet/api/system.span-1.fill) method of [Span](https://docs.microsoft.com/en-us/dotnet/api/system.span-1):\r\n```cs\r\npublic void Reset()\r\n{\r\n\t_buffer.Fill(0ul);\r\n}\r\n```\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeter-juhasz%2Fvalue-bit-array","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeter-juhasz%2Fvalue-bit-array","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeter-juhasz%2Fvalue-bit-array/lists"}