124 lines
2.8 KiB
Go
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,
|
|
}
|
|
}
|