bitmap

package
v0.0.27 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package bitmap implements the Arrow validity bitmap.

A bitmap holds one bit per value, packed least significant bit first within each byte, which is the layout the Arrow columnar specification requires. A set bit means the value at that position is valid, and a clear bit means it is null.

The zero Bitmap is empty and ready to use. Append grows it.

Bits past the length live in the last byte and are always zero. Every operation that could leave them set clears them again before returning, so CountOnes and Bytes never have to think about it. This is not merely tidy: Arrow requires the padding to be zero for a buffer to round trip through IPC correctly.

Stability: tier 1, stable.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bitmap

type Bitmap struct {
	// contains filtered or unexported fields
}

Bitmap is a growable sequence of bits.

Example
package main

import (
	"fmt"

	"github.com/tamnd/kuma/bitmap"
)

func main() {
	// A column of five values where the third one is missing.
	valid := bitmap.NewSet(5)
	valid.Set(2, false)

	fmt.Println(valid.Len(), valid.CountOnes())
	fmt.Println(valid.Get(1), valid.Get(2))
}
Output:
5 4
true false

func FromBytes

func FromBytes(buf []byte, n int) *Bitmap

FromBytes wraps an existing byte slice as a bitmap of n bits without copying. The caller must not modify buf afterwards. It panics if buf is too short to hold n bits.

func New

func New(n int) *Bitmap

New returns a bitmap of n bits, all clear.

func NewSet

func NewSet(n int) *Bitmap

NewSet returns a bitmap of n bits, all set. This is the common case for a column with no nulls, so it is worth having rather than looping over New.

func (*Bitmap) And

func (b *Bitmap) And(other *Bitmap)

And sets b to the intersection of b and other. Both must have the same length. This is the operation that combines two validity bitmaps in a binary kernel, where a result is valid only if both inputs were.

Example
package main

import (
	"fmt"

	"github.com/tamnd/kuma/bitmap"
)

func main() {
	// Adding two columns: the result is valid only where both inputs were.
	a := bitmap.NewSet(4)
	a.Set(1, false)

	b := bitmap.NewSet(4)
	b.Set(3, false)

	a.And(b)

	for i := range a.Len() {
		fmt.Print(a.Get(i), " ")
	}
	fmt.Println()
}
Output:
true false true false

func (*Bitmap) AndNot

func (b *Bitmap) AndNot(other *Bitmap)

AndNot clears in b every bit that is set in other. Both must have the same length.

func (*Bitmap) Append

func (b *Bitmap) Append(v bool)

Append adds one bit to the end.

Example
package main

import (
	"fmt"

	"github.com/tamnd/kuma/bitmap"
)

func main() {
	// The zero value is ready to use.
	var b bitmap.Bitmap
	for _, v := range []bool{true, false, true} {
		b.Append(v)
	}

	fmt.Println(b.Len(), b.CountOnes())
}
Output:
3 2

func (*Bitmap) Bytes

func (b *Bitmap) Bytes() []byte

Bytes returns the underlying storage. The padding bits in the final byte are zero. Modifying the result modifies the bitmap.

func (*Bitmap) Clone

func (b *Bitmap) Clone() *Bitmap

Clone returns a copy that shares no memory with b.

func (*Bitmap) CountOnes

func (b *Bitmap) CountOnes() int

CountOnes returns the number of set bits, which for a validity bitmap is the number of valid values.

It reads the whole buffer including the final byte, which is correct because the bits past the length there are always zero.

func (*Bitmap) CountOnesRange

func (b *Bitmap) CountOnesRange(i, j int) int

CountOnesRange returns the number of set bits in positions i through j-1. It panics if the range is out of bounds.

This is what a column reports as its null count after being sliced. Slicing is meant to be constant time, so the array layer keeps an offset into a bitmap it shares rather than copying one, and then it needs the count over a range that does not start on a byte boundary.

func (*Bitmap) Get

func (b *Bitmap) Get(i int) bool

Get reports whether bit i is set. It panics if i is out of range, matching the behavior of an ordinary slice index.

func (*Bitmap) Len

func (b *Bitmap) Len() int

Len returns the number of bits.

func (*Bitmap) Not

func (b *Bitmap) Not()

Not inverts every bit.

func (*Bitmap) Or

func (b *Bitmap) Or(other *Bitmap)

Or sets b to the union of b and other. Both must have the same length.

func (*Bitmap) Set

func (b *Bitmap) Set(i int, v bool)

Set sets bit i to v. It panics if i is out of range.

func (*Bitmap) Slice

func (b *Bitmap) Slice(i, j int) *Bitmap

Slice returns bits i through j-1 as a new bitmap, renumbered so that bit i becomes bit 0. It panics if the range is out of bounds.

It copies rather than aliasing. A bitmap could carry a starting bit offset and slice in constant time, which is what an Arrow array does, but then Bytes would no longer return a buffer that begins at bit zero with zeroed padding, and every operation and every caller would have to carry an offset that is almost always zero. Copying costs one byte per eight bits, so slicing a chunk of eight thousand rows moves a kilobyte. The layers above this one keep their own offset for the places where constant time slicing is worth the complexity.

Example
package main

import (
	"fmt"

	"github.com/tamnd/kuma/bitmap"
)

func main() {
	valid := bitmap.NewSet(10)
	valid.Set(4, false)
	valid.Set(7, false)

	// Rows 3 through 7 of the column, renumbered from zero.
	window := valid.Slice(3, 8)

	for i := range window.Len() {
		fmt.Print(window.Get(i), " ")
	}
	fmt.Println()
}
Output:
true false true true false

func (*Bitmap) Xor

func (b *Bitmap) Xor(other *Bitmap)

Xor sets b to the symmetric difference of b and other, meaning the bits that are set in exactly one of them. Both must have the same length.

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

Builder accumulates bits and hands over the finished Bitmap.

Bitmap.Append works one bit at a time, which is the right shape when the values arrive one at a time. A builder exists for the case that dominates when a column is being read, which is a long run of the same bit: a chunk with no nulls is n set bits in a row, and AppendMany writes the whole bytes of that run as a fill instead of n calls that each recompute the same byte index and mask.

The zero Builder is empty and ready to use.

Example
package main

import (
	"fmt"

	"github.com/tamnd/kuma/bitmap"
)

func main() {
	// Reading a column of a thousand values where only the last ten are
	// missing. The long run of valid values is one call rather than a loop.
	var b bitmap.Builder
	b.Grow(1000)
	b.AppendMany(true, 990)
	b.AppendMany(false, 10)

	valid := b.Finish()
	fmt.Println(valid.Len(), valid.CountOnes())
}
Output:
1000 990

func (*Builder) Append

func (b *Builder) Append(v bool)

Append adds one bit to the end.

func (*Builder) AppendBools

func (b *Builder) AppendBools(vals []bool)

AppendBools adds one bit per element of vals.

func (*Builder) AppendMany

func (b *Builder) AppendMany(v bool, n int)

AppendMany adds n copies of v to the end. AppendMany(true, n) is how a column with no nulls builds its validity bitmap, so it is the path worth keeping fast.

func (*Builder) Finish

func (b *Builder) Finish() *Bitmap

Finish returns the accumulated bits and resets the builder to empty.

The returned bitmap takes over the buffer rather than copying it, so a builder cannot be used to observe or modify a bitmap it has already handed over. Call Finish once per bitmap.

func (*Builder) Grow

func (b *Builder) Grow(n int)

Grow makes room for n more bits without appending them. It is worth calling when the final length is known, and harmless when it is only a guess.

func (*Builder) Len

func (b *Builder) Len() int

Len returns the number of bits appended so far.

func (*Builder) Reset

func (b *Builder) Reset()

Reset drops the accumulated bits and keeps the buffer for reuse.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL