Initial
This commit is contained in:
125
internal/statuslist/list.go
Normal file
125
internal/statuslist/list.go
Normal file
@@ -0,0 +1,125 @@
|
||||
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
|
||||
}
|
||||
51
internal/statuslist/list_test.go
Normal file
51
internal/statuslist/list_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package statuslist
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAppendSetGet(t *testing.T) {
|
||||
list := New()
|
||||
if idx := list.Dodaj(false); idx != 0 {
|
||||
t.Fatalf("expected index 0, got %d", idx)
|
||||
}
|
||||
if idx := list.Dodaj(true); idx != 1 {
|
||||
t.Fatalf("expected index 1, got %d", idx)
|
||||
}
|
||||
|
||||
if err := list.Nastavi(0, true); err != nil {
|
||||
t.Fatalf("set error: %v", err)
|
||||
}
|
||||
|
||||
v0, err := list.Dobi(0)
|
||||
if err != nil {
|
||||
t.Fatalf("get error: %v", err)
|
||||
}
|
||||
if !v0 {
|
||||
t.Fatalf("expected true on index 0")
|
||||
}
|
||||
|
||||
v1, err := list.Dobi(1)
|
||||
if err != nil {
|
||||
t.Fatalf("get error: %v", err)
|
||||
}
|
||||
if !v1 {
|
||||
t.Fatalf("expected true on index 1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodedListProducesBase64(t *testing.T) {
|
||||
list := New()
|
||||
list.Dodaj(true)
|
||||
list.Dodaj(false)
|
||||
list.Dodaj(true)
|
||||
|
||||
encoded, err := list.KodiranSeznam()
|
||||
if err != nil {
|
||||
t.Fatalf("encoded list error: %v", err)
|
||||
}
|
||||
if _, err := base64.StdEncoding.DecodeString(encoded); err != nil {
|
||||
t.Fatalf("expected valid base64: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user