126 lines
2.8 KiB
Go
126 lines
2.8 KiB
Go
package statuslist
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"encoding/base64"
|
|
"errors"
|
|
)
|
|
|
|
var (
|
|
ErrIndexOutOfRange = errors.New("index out of range")
|
|
)
|
|
|
|
type StatusList struct {
|
|
data []byte
|
|
bitLength int
|
|
}
|
|
|
|
// New vrne prazno bitno listo statusov.
|
|
func New() *StatusList {
|
|
return &StatusList{
|
|
data: make([]byte, 0),
|
|
bitLength: 0,
|
|
}
|
|
}
|
|
|
|
// IzSurovihPodatkov zgradi listo iz bajtov in dolzine v bitih.
|
|
func IzSurovihPodatkov(data []byte, bitLength int) (*StatusList, error) {
|
|
if bitLength < 0 {
|
|
return nil, errors.New("bitLength must be >= 0")
|
|
}
|
|
minBytes := steviloBajtovZaBite(bitLength)
|
|
if len(data) < minBytes {
|
|
return nil, errors.New("invalid data size for bitLength")
|
|
}
|
|
|
|
cloned := make([]byte, minBytes)
|
|
copy(cloned, data[:minBytes])
|
|
return &StatusList{data: cloned, bitLength: bitLength}, nil
|
|
}
|
|
|
|
// Dodaj doda novo stanje in vrne njegov index.
|
|
func (s *StatusList) Dodaj(value bool) int {
|
|
index := s.bitLength
|
|
s.zagotoviIndex(index)
|
|
s.nastaviBit(index, value)
|
|
s.bitLength++
|
|
return index
|
|
}
|
|
|
|
// Nastavi spremeni obstojece stanje na podanem indexu.
|
|
func (s *StatusList) Nastavi(index int, value bool) error {
|
|
if index < 0 || index >= s.bitLength {
|
|
return ErrIndexOutOfRange
|
|
}
|
|
s.nastaviBit(index, value)
|
|
return nil
|
|
}
|
|
|
|
// Dobi vrne stanje na podanem indexu.
|
|
func (s *StatusList) Dobi(index int) (bool, error) {
|
|
if index < 0 || index >= s.bitLength {
|
|
return false, ErrIndexOutOfRange
|
|
}
|
|
return s.dobiBit(index), nil
|
|
}
|
|
|
|
// DolzinaBitov vrne stevilo aktivnih statusov.
|
|
func (s *StatusList) DolzinaBitov() int {
|
|
return s.bitLength
|
|
}
|
|
|
|
// Bajti vrne kopijo notranjih bajtov liste.
|
|
func (s *StatusList) Bajti() []byte {
|
|
out := make([]byte, len(s.data))
|
|
copy(out, s.data)
|
|
return out
|
|
}
|
|
|
|
// KodiranSeznam vrne base64(gzip(byteArray)) predstavitev liste.
|
|
func (s *StatusList) KodiranSeznam() (string, error) {
|
|
var buf bytes.Buffer
|
|
zip := gzip.NewWriter(&buf)
|
|
if _, err := zip.Write(s.data); err != nil {
|
|
return "", err
|
|
}
|
|
if err := zip.Close(); err != nil {
|
|
return "", err
|
|
}
|
|
return base64.StdEncoding.EncodeToString(buf.Bytes()), nil
|
|
}
|
|
|
|
// zagotoviIndex razsiri notranji buffer za podani index.
|
|
func (s *StatusList) zagotoviIndex(index int) {
|
|
required := steviloBajtovZaBite(index + 1)
|
|
for len(s.data) < required {
|
|
s.data = append(s.data, 0)
|
|
}
|
|
}
|
|
|
|
// nastaviBit vklopi ali izklopi bit na podanem indexu.
|
|
func (s *StatusList) nastaviBit(index int, value bool) {
|
|
byteIdx := index / 8
|
|
mask := byte(1 << (index % 8))
|
|
if value {
|
|
s.data[byteIdx] |= mask
|
|
return
|
|
}
|
|
s.data[byteIdx] &^= mask
|
|
}
|
|
|
|
// dobiBit prebere bit na podanem indexu.
|
|
func (s *StatusList) dobiBit(index int) bool {
|
|
byteIdx := index / 8
|
|
mask := byte(1 << (index % 8))
|
|
return s.data[byteIdx]&mask != 0
|
|
}
|
|
|
|
// steviloBajtovZaBite izracuna koliko bajtov potrebujemo za bite.
|
|
func steviloBajtovZaBite(bitLength int) int {
|
|
if bitLength == 0 {
|
|
return 0
|
|
}
|
|
return (bitLength + 7) / 8
|
|
}
|