blob: 9836364473a81ca76edac39e448ab58fefffc9b9 [file] [log] [blame]
package plist
import (
"hash/crc32"
"sort"
"time"
"strconv"
)
// magic value used in the non-binary encoding of UIDs
// (stored as a dictionary mapping CF$UID->integer)
const cfUIDMagic = "CF$UID"
type cfValue interface {
typeName() string
hash() interface{}
}
type cfDictionary struct {
keys sort.StringSlice
values []cfValue
}
func (*cfDictionary) typeName() string {
return "dictionary"
}
func (p *cfDictionary) hash() interface{} {
return p
}
func (p *cfDictionary) Len() int {
return len(p.keys)
}
func (p *cfDictionary) Less(i, j int) bool {
return p.keys.Less(i, j)
}
func (p *cfDictionary) Swap(i, j int) {
p.keys.Swap(i, j)
p.values[i], p.values[j] = p.values[j], p.values[i]
}
func (p *cfDictionary) sort() {
sort.Sort(p)
}
func (p *cfDictionary) maybeUID(lax bool) cfValue {
if len(p.keys) == 1 && p.keys[0] == "CF$UID" && len(p.values) == 1 {
pval := p.values[0]
if integer, ok := pval.(*cfNumber); ok {
return cfUID(integer.value)
}
// Openstep only has cfString. Act like the unmarshaller a bit.
if lax {
if str, ok := pval.(cfString); ok {
if i, err := strconv.ParseUint(string(str), 10, 64); err == nil {
return cfUID(i)
}
}
}
}
return p
}
type cfArray struct {
values []cfValue
}
func (*cfArray) typeName() string {
return "array"
}
func (p *cfArray) hash() interface{} {
return p
}
type cfString string
func (cfString) typeName() string {
return "string"
}
func (p cfString) hash() interface{} {
return string(p)
}
type cfNumber struct {
signed bool
value uint64
}
func (*cfNumber) typeName() string {
return "integer"
}
func (p *cfNumber) hash() interface{} {
if p.signed {
return int64(p.value)
}
return p.value
}
type cfReal struct {
wide bool
value float64
}
func (cfReal) typeName() string {
return "real"
}
func (p *cfReal) hash() interface{} {
if p.wide {
return p.value
}
return float32(p.value)
}
type cfBoolean bool
func (cfBoolean) typeName() string {
return "boolean"
}
func (p cfBoolean) hash() interface{} {
return bool(p)
}
type cfUID UID
func (cfUID) typeName() string {
return "UID"
}
func (p cfUID) hash() interface{} {
return p
}
func (p cfUID) toDict() *cfDictionary {
return &cfDictionary{
keys: []string{cfUIDMagic},
values: []cfValue{&cfNumber{
signed: false,
value: uint64(p),
}},
}
}
type cfData []byte
func (cfData) typeName() string {
return "data"
}
func (p cfData) hash() interface{} {
// Data are uniqued by their checksums.
// Todo: Look at calculating this only once and storing it somewhere;
// crc32 is fairly quick, however.
return crc32.ChecksumIEEE([]byte(p))
}
type cfDate time.Time
func (cfDate) typeName() string {
return "date"
}
func (p cfDate) hash() interface{} {
return time.Time(p)
}