heroagent/pkg/data/dedupestor/metadata.go
2025-04-23 04:18:28 +02:00

124 lines
2.8 KiB
Go

// Package dedupestor provides a key-value store with deduplication based on content hashing
package dedupestor
import (
"encoding/binary"
)
// Metadata represents a stored value with its ID and references
type Metadata struct {
ID uint32 // ID of the stored data in the database
References []Reference // List of references to this data
}
// Reference represents a reference to stored data
type Reference struct {
Owner uint16 // Owner identifier
ID uint32 // Reference identifier
}
// ToBytes converts Metadata to bytes for storage
func (m Metadata) ToBytes() []byte {
// Calculate size: 4 bytes for ID + 6 bytes per reference
size := 4 + (len(m.References) * 6)
result := make([]byte, size)
// Write ID (4 bytes)
binary.LittleEndian.PutUint32(result[0:4], m.ID)
// Write references (6 bytes each)
offset := 4
for _, ref := range m.References {
refBytes := ref.ToBytes()
copy(result[offset:offset+6], refBytes)
offset += 6
}
return result
}
// BytesToMetadata converts bytes back to Metadata
func BytesToMetadata(b []byte) Metadata {
if len(b) < 4 {
return Metadata{
ID: 0,
References: []Reference{},
}
}
id := binary.LittleEndian.Uint32(b[0:4])
refs := []Reference{}
// Parse references (each reference is 6 bytes)
for i := 4; i < len(b); i += 6 {
if i+6 <= len(b) {
refs = append(refs, BytesToReference(b[i:i+6]))
}
}
return Metadata{
ID: id,
References: refs,
}
}
// AddReference adds a new reference if it doesn't already exist
func (m Metadata) AddReference(ref Reference) (Metadata, error) {
// Check if reference already exists
for _, existing := range m.References {
if existing.Owner == ref.Owner && existing.ID == ref.ID {
return m, nil
}
}
// Add the new reference
newRefs := append(m.References, ref)
return Metadata{
ID: m.ID,
References: newRefs,
}, nil
}
// RemoveReference removes a reference if it exists
func (m Metadata) RemoveReference(ref Reference) (Metadata, error) {
newRefs := []Reference{}
for _, existing := range m.References {
if existing.Owner != ref.Owner || existing.ID != ref.ID {
newRefs = append(newRefs, existing)
}
}
return Metadata{
ID: m.ID,
References: newRefs,
}, nil
}
// ToBytes converts Reference to bytes
func (r Reference) ToBytes() []byte {
result := make([]byte, 6)
// Write owner (2 bytes)
binary.LittleEndian.PutUint16(result[0:2], r.Owner)
// Write ID (4 bytes)
binary.LittleEndian.PutUint32(result[2:6], r.ID)
return result
}
// BytesToReference converts bytes to Reference
func BytesToReference(b []byte) Reference {
if len(b) < 6 {
return Reference{}
}
owner := binary.LittleEndian.Uint16(b[0:2])
id := binary.LittleEndian.Uint32(b[2:6])
return Reference{
Owner: owner,
ID: id,
}
}