95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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
|
||
|
|
}
|