// ================================================================ // 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 int64 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 (omap *OrderedMap) IsEmpty() bool { return omap.FieldCount == 0 } func (omap *OrderedMap) Has(key string) bool { return omap.findEntry(&key) != nil } func (omap *OrderedMap) findEntry(key *string) *orderedMapEntry { if omap.keysToEntries != nil { return omap.keysToEntries[*key] } else { for pe := omap.Head; pe != nil; pe = pe.Next { if pe.Key == *key { return pe } } return nil } } // ---------------------------------------------------------------- func (omap *OrderedMap) Put(key string, value interface{}) { pe := omap.findEntry(&key) if pe == nil { pe = newOrderedMapEntry(&key, value) if omap.Head == nil { omap.Head = pe omap.Tail = pe } else { pe.Prev = omap.Tail pe.Next = nil omap.Tail.Next = pe omap.Tail = pe } if omap.keysToEntries != nil { omap.keysToEntries[key] = pe } omap.FieldCount++ } else { pe.Value = value } } // ---------------------------------------------------------------- func (omap *OrderedMap) Get(key string) interface{} { pe := omap.findEntry(&key) if pe == nil { return nil } else { return pe.Value } } // 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 (omap *OrderedMap) GetWithCheck(key string) (interface{}, bool) { pe := omap.findEntry(&key) if pe == nil { return nil, false } else { return pe.Value, true } } func (omap *OrderedMap) GetKeys() []string { keys := make([]string, omap.FieldCount) i := 0 for pe := omap.Head; pe != nil; pe = pe.Next { keys[i] = pe.Key i++ } return keys } // Returns an array of keys, not including the ones specified. The ones // specified are to be passed in as a map from string to bool, as Go // doesn't have hash-sets. func (omap *OrderedMap) GetKeysExcept(exceptions map[string]bool) []string { keys := make([]string, 0) for pe := omap.Head; pe != nil; pe = pe.Next { if _, present := exceptions[pe.Key]; !present { keys = append(keys, pe.Key) } } return keys } // ---------------------------------------------------------------- func (omap *OrderedMap) Clear() { omap.FieldCount = 0 omap.Head = nil omap.Tail = nil } // ---------------------------------------------------------------- // Returns true if it was found and removed func (omap *OrderedMap) Remove(key string) bool { pe := omap.findEntry(&key) if pe == nil { return false } else { omap.unlink(pe) return true } } // ---------------------------------------------------------------- func (omap *OrderedMap) unlink(pe *orderedMapEntry) { if pe == omap.Head { if pe == omap.Tail { omap.Head = nil omap.Tail = nil } else { omap.Head = pe.Next pe.Next.Prev = nil } } else { pe.Prev.Next = pe.Next if pe == omap.Tail { omap.Tail = pe.Prev } else { pe.Next.Prev = pe.Prev } } if omap.keysToEntries != nil { delete(omap.keysToEntries, pe.Key) } omap.FieldCount-- }