forked from TrueCloudLab/neoneo-go
167 lines
3.4 KiB
Go
167 lines
3.4 KiB
Go
|
package stack
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"sort"
|
||
|
|
||
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||
|
)
|
||
|
|
||
|
// Map represents a map of key, value pair on the stack.
|
||
|
// Both key and value are stack Items.
|
||
|
type Map struct {
|
||
|
*abstractItem
|
||
|
val map[Item]Item
|
||
|
}
|
||
|
|
||
|
// NewMap returns a Map stack Item given
|
||
|
// a map whose keys and values are stack Items.
|
||
|
func NewMap(val map[Item]Item) (*Map, error) {
|
||
|
return &Map{
|
||
|
abstractItem: &abstractItem{},
|
||
|
val: val,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// Map will overwrite the default implementation
|
||
|
// to allow go to cast this item as an Map.
|
||
|
func (m *Map) Map() (*Map, error) {
|
||
|
return m, nil
|
||
|
}
|
||
|
|
||
|
// Boolean overrides the default Boolean method
|
||
|
// to convert an Map into a Boolean StackItem
|
||
|
func (m *Map) Boolean() (*Boolean, error) {
|
||
|
return NewBoolean(true), nil
|
||
|
}
|
||
|
|
||
|
// ContainsKey returns a boolean whose value is true
|
||
|
// iff the underlying map value contains the Item i
|
||
|
// as a key.
|
||
|
func (m *Map) ContainsKey(key Item) (*Boolean, error) {
|
||
|
for k := range m.Value() {
|
||
|
if ok, err := CompareHash(k, key); err != nil {
|
||
|
return nil, err
|
||
|
} else if ok.Value() == true {
|
||
|
return ok, nil
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return NewBoolean(false), nil
|
||
|
}
|
||
|
|
||
|
// Value returns the underlying map's value
|
||
|
func (m *Map) Value() map[Item]Item {
|
||
|
return m.val
|
||
|
}
|
||
|
|
||
|
// Remove removes the Item i from the
|
||
|
// underlying map's value.
|
||
|
func (m *Map) Remove(key Item) error {
|
||
|
var d Item
|
||
|
for k := range m.Value() {
|
||
|
if ok, err := CompareHash(k, key); err != nil {
|
||
|
return err
|
||
|
} else if ok.Value() == true {
|
||
|
d = k
|
||
|
}
|
||
|
|
||
|
}
|
||
|
if d != nil {
|
||
|
delete(m.Value(), d)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Add inserts a new key, value pair of Items into
|
||
|
// the underlying map's value.
|
||
|
func (m *Map) Add(key Item, value Item) error {
|
||
|
for k := range m.Value() {
|
||
|
if ok, err := CompareHash(k, key); err != nil {
|
||
|
return err
|
||
|
} else if ok.Value() == true {
|
||
|
return errors.New("try to insert duplicate key! ")
|
||
|
}
|
||
|
}
|
||
|
m.Value()[key] = value
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ValueOfKey tries to get the value of the key Item
|
||
|
// from the map's underlying value.
|
||
|
func (m *Map) ValueOfKey(key Item) (Item, error) {
|
||
|
for k, v := range m.Value() {
|
||
|
if ok, err := CompareHash(k, key); err != nil {
|
||
|
return nil, err
|
||
|
} else if ok.Value() == true {
|
||
|
return v, nil
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return nil, nil
|
||
|
|
||
|
}
|
||
|
|
||
|
// Clear empties the the underlying map's value.
|
||
|
func (m *Map) Clear() {
|
||
|
m.val = map[Item]Item{}
|
||
|
}
|
||
|
|
||
|
// CompareHash compare the the Hashes of two items.
|
||
|
// If they are equal it returns a true boolean. Otherwise
|
||
|
// it returns false boolean. Item whose hashes are equal are
|
||
|
// to be considered equal.
|
||
|
func CompareHash(i1 Item, i2 Item) (*Boolean, error) {
|
||
|
hash1, err := i1.Hash()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
hash2, err := i2.Hash()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if hash1 == hash2 {
|
||
|
return NewBoolean(true), nil
|
||
|
}
|
||
|
|
||
|
return NewBoolean(false), nil
|
||
|
}
|
||
|
|
||
|
// Hash overrides the default abstract hash method.
|
||
|
func (m *Map) Hash() (string, error) {
|
||
|
var hashSlice sort.StringSlice = []string{}
|
||
|
var data = fmt.Sprintf("%T ", m)
|
||
|
|
||
|
for k, v := range m.Value() {
|
||
|
hk, err := k.Hash()
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
hv, err := v.Hash()
|
||
|
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
hashSlice = append(hashSlice, hk)
|
||
|
hashSlice = append(hashSlice, hv)
|
||
|
}
|
||
|
hashSlice.Sort()
|
||
|
|
||
|
for _, h := range hashSlice {
|
||
|
data += h
|
||
|
}
|
||
|
|
||
|
return KeyGenerator([]byte(data))
|
||
|
}
|
||
|
|
||
|
// KeyGenerator hashes a byte slice to obtain a unique identifier.
|
||
|
func KeyGenerator(data []byte) (string, error) {
|
||
|
h, err := hash.Sha256([]byte(data))
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return h.String(), nil
|
||
|
}
|