blob: cb758b275f8dfc3f8c33a335b62d214399519012 [file] [log] [blame]
// Copyright 2018 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package graph
import (
"fmt"
"strings"
)
// KeySet is a set of all keys ever defined in a graph.
//
// Each key is a singleton object: asking for the same key twice returns exact
// same *Key object. This simplifies comparison of keys and using keys as, well,
// keys in a map.
type KeySet struct {
keys map[string]*Key // compact representation of the key path -> *Key
}
// Key returns a *Key given a list of (kind, id) pairs.
//
// Assumes strings don't have zero bytes. No other restrictions.
func (k *KeySet) Key(pairs ...string) (*Key, error) {
switch {
case len(pairs) == 0:
return nil, fmt.Errorf("empty key path")
case len(pairs)%2 != 0:
return nil, fmt.Errorf("key path %q has odd number of components", pairs)
}
for _, s := range pairs {
if strings.IndexByte(s, 0) != -1 {
return nil, fmt.Errorf("bad key path element %q, has zero byte inside", s)
}
}
keyID := strings.Join(pairs, "\x00")
if key := k.keys[keyID]; key != nil {
return key, nil
}
if k.keys == nil {
k.keys = make(map[string]*Key, 1)
}
key := &Key{set: k, pairs: pairs, idx: len(k.keys), cmp: keyID}
k.keys[keyID] = key
return key, nil
}