miller/go/src/lib/ordered-map.go
2021-02-14 01:13:24 -05:00

153 lines
3.5 KiB
Go

// ================================================================
// ORDERED MAP FROM STRING TO INTERFACE{}
//
// Quite like types.OrderedMap but only with interface{} keys. See orderedMap.go for
// more information.
// ================================================================
package lib
// ----------------------------------------------------------------
type OrderedMap struct {
FieldCount int
Head *orderedMapEntry
Tail *orderedMapEntry
keysToEntries map[string]*orderedMapEntry
}
type orderedMapEntry struct {
Key string
Value interface{}
Prev *orderedMapEntry
Next *orderedMapEntry
}
// ----------------------------------------------------------------
func NewOrderedMap() *OrderedMap {
return &OrderedMap{
FieldCount: 0,
Head: nil,
Tail: nil,
keysToEntries: make(map[string]*orderedMapEntry),
}
}
// ----------------------------------------------------------------
// Value-copy is up to the caller -- PutReference and PutCopy
// are in the public OrderedMap API.
func newOrderedMapEntry(key *string, value interface{}) *orderedMapEntry {
return &orderedMapEntry{
*key,
value,
nil,
nil,
}
}
// ----------------------------------------------------------------
func (this *OrderedMap) Has(key string) bool {
return this.findEntry(&key) != nil
}
func (this *OrderedMap) findEntry(key *string) *orderedMapEntry {
if this.keysToEntries != nil {
return this.keysToEntries[*key]
} else {
for pe := this.Head; pe != nil; pe = pe.Next {
if pe.Key == *key {
return pe
}
}
return nil
}
}
// ----------------------------------------------------------------
func (this *OrderedMap) Put(key string, value interface{}) {
pe := this.findEntry(&key)
if pe == nil {
pe = newOrderedMapEntry(&key, value)
if this.Head == nil {
this.Head = pe
this.Tail = pe
} else {
pe.Prev = this.Tail
pe.Next = nil
this.Tail.Next = pe
this.Tail = pe
}
if this.keysToEntries != nil {
this.keysToEntries[key] = pe
}
this.FieldCount++
} else {
pe.Value = value
}
}
// ----------------------------------------------------------------
func (this *OrderedMap) Get(key string) interface{} {
pe := this.findEntry(&key)
if pe == nil {
return nil
} else {
return pe.Value
}
return nil
}
// The Get is sufficient for pointer values -- the caller can check if the
// return value is nil. For int/string values (which are non-nullable) we have
// this method.
func (this *OrderedMap) GetWithCheck(key string) (interface{}, bool) {
pe := this.findEntry(&key)
if pe == nil {
return nil, false
} else {
return pe.Value, true
}
return nil, false
}
// ----------------------------------------------------------------
func (this *OrderedMap) Clear() {
this.FieldCount = 0
this.Head = nil
this.Tail = nil
}
// ----------------------------------------------------------------
// Returns true if it was found and removed
func (this *OrderedMap) Remove(key string) bool {
pe := this.findEntry(&key)
if pe == nil {
return false
} else {
this.unlink(pe)
return true
}
}
// ----------------------------------------------------------------
func (this *OrderedMap) unlink(pe *orderedMapEntry) {
if pe == this.Head {
if pe == this.Tail {
this.Head = nil
this.Tail = nil
} else {
this.Head = pe.Next
pe.Next.Prev = nil
}
} else {
pe.Prev.Next = pe.Next
if pe == this.Tail {
this.Tail = pe.Prev
} else {
pe.Next.Prev = pe.Prev
}
}
if this.keysToEntries != nil {
delete(this.keysToEntries, pe.Key)
}
this.FieldCount--
}