77 lines
2.1 KiB
Go
77 lines
2.1 KiB
Go
|
package set
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/gob"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
// GobEncode is an implementation of the interface gob.GobEncoder, allowing
|
||
|
// sets to be included in structures encoded via gob.
|
||
|
//
|
||
|
// The set rules are included in the serialized value, so the caller must
|
||
|
// register its concrete rules type with gob.Register before using a
|
||
|
// set in a gob, and possibly also implement GobEncode/GobDecode to customize
|
||
|
// how any parameters are persisted.
|
||
|
//
|
||
|
// The set elements are also included, so if they are of non-primitive types
|
||
|
// they too must be registered with gob.
|
||
|
//
|
||
|
// If the produced gob values will persist for a long time, the caller must
|
||
|
// ensure compatibility of the rules implementation. In particular, if the
|
||
|
// definition of element equivalence changes between encoding and decoding
|
||
|
// then two distinct stored elements may be considered equivalent on decoding,
|
||
|
// causing the recovered set to have fewer elements than when it was stored.
|
||
|
func (s Set) GobEncode() ([]byte, error) {
|
||
|
gs := gobSet{
|
||
|
Version: 0,
|
||
|
Rules: s.rules,
|
||
|
Values: s.Values(),
|
||
|
}
|
||
|
|
||
|
buf := &bytes.Buffer{}
|
||
|
enc := gob.NewEncoder(buf)
|
||
|
err := enc.Encode(gs)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("error encoding set.Set: %s", err)
|
||
|
}
|
||
|
|
||
|
return buf.Bytes(), nil
|
||
|
}
|
||
|
|
||
|
// GobDecode is the opposite of GobEncode. See GobEncode for information
|
||
|
// on the requirements for and caveats of including set values in gobs.
|
||
|
func (s *Set) GobDecode(buf []byte) error {
|
||
|
r := bytes.NewReader(buf)
|
||
|
dec := gob.NewDecoder(r)
|
||
|
|
||
|
var gs gobSet
|
||
|
err := dec.Decode(&gs)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error decoding set.Set: %s", err)
|
||
|
}
|
||
|
if gs.Version != 0 {
|
||
|
return fmt.Errorf("unsupported set.Set encoding version %d; need 0", gs.Version)
|
||
|
}
|
||
|
|
||
|
victim := NewSetFromSlice(gs.Rules, gs.Values)
|
||
|
s.vals = victim.vals
|
||
|
s.rules = victim.rules
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type gobSet struct {
|
||
|
Version int
|
||
|
Rules Rules
|
||
|
|
||
|
// The bucket-based representation is for efficient in-memory access, but
|
||
|
// for serialization it's enough to just retain the values themselves,
|
||
|
// which we can re-bucket using the rules (which may have changed!) when
|
||
|
// we re-inflate.
|
||
|
Values []interface{}
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
gob.Register([]interface{}(nil))
|
||
|
}
|