// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package nfs4 import ( "slices" "sourcedock.dev/petrbalvin/nfs/internal/xdr" ) // maxBitmapWords is the word count limit of a bitmap4. The protocol caps // the array at 100 words; the attribute table today ends far below that. const maxBitmapWords = 100 // A Bitmap is a bitmap4: a compact set of attribute numbers. type Bitmap []uint32 // OfBits returns a Bitmap carrying exactly the given attribute numbers. func OfBits(bits ...uint32) Bitmap { if len(bits) == 0 { return Bitmap{} } var words Bitmap for _, n := range bits { w := int(n / 32) for len(words) <= w { words = append(words, 0) } words[w] |= 1 << (n % 32) } return words } // Has reports whether the attribute number n is set. func (m Bitmap) Has(n uint32) bool { w := int(n / 32) return w < len(m) && m[w]&(1<<(n%32)) != 0 } // With returns a bitmap carrying n in addition to what was already set. // Words the original bitmap already held are never dropped, even when the // new bit lands in a lower word. func (m Bitmap) With(n uint32) Bitmap { w := int(n / 32) out := make(Bitmap, max(w+1, len(m))) copy(out, m) out[w] |= 1 << (n % 32) return out } // AppendTo appends the bitmap in wire form: a count of words followed by // the words, with trailing zero words trimmed. func (m Bitmap) AppendTo(b []byte) []byte { trimmed := m for len(trimmed) > 0 && trimmed[len(trimmed)-1] == 0 { trimmed = trimmed[:len(trimmed)-1] } b = xdr.AppendUint32(b, uint32(len(trimmed))) for _, w := range trimmed { b = xdr.AppendUint32(b, w) } return b } // ReadBitmap reads a bitmap4. A bitmap beyond the word limit is refused // before any of it is buffered. func ReadBitmap(d *xdr.Decoder) (Bitmap, error) { n, err := d.Uint32() if err != nil { return nil, err } if n > maxBitmapWords { return nil, xdr.ErrBadLength } m := make(Bitmap, 0, n) for range n { w, err := d.Uint32() if err != nil { return nil, err } m = append(m, w) } return m, nil } // Words reports the words of the bitmap, without trailing zero words. func (m Bitmap) Words() []uint32 { trimmed := slices.Clone(m) for len(trimmed) > 0 && trimmed[len(trimmed)-1] == 0 { trimmed = trimmed[:len(trimmed)-1] } return trimmed }