| // Copyright 2022 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package benchproc | |
| import "strings" | |
| // A Key is an immutable tuple mapping from Fields to strings whose | |
| // structure is given by a Projection. Two Keys are == if they come | |
| // from the same Projection and have identical values. | |
| type Key struct { | |
| k *keyNode | |
| } | |
| // IsZero reports whether k is a zeroed Key with no projection and no fields. | |
| func (k Key) IsZero() bool { | |
| return k.k == nil | |
| } | |
| // Get returns the value of Field f in this Key. | |
| // | |
| // It panics if Field f does not come from the same Projection as the | |
| // Key or if f is a tuple Field. | |
| func (k Key) Get(f *Field) string { | |
| if k.IsZero() { | |
| panic("zero Key has no fields") | |
| } | |
| if k.k.proj != f.proj { | |
| panic("Key and Field have different Projections") | |
| } | |
| if f.IsTuple { | |
| panic(f.Name + " is a tuple field") | |
| } | |
| idx := f.idx | |
| if idx >= len(k.k.vals) { | |
| return "" | |
| } | |
| return k.k.vals[idx] | |
| } | |
| // Projection returns the Projection describing Key k. | |
| func (k Key) Projection() *Projection { | |
| if k.IsZero() { | |
| return nil | |
| } | |
| return k.k.proj | |
| } | |
| // String returns Key as a space-separated sequence of key:value | |
| // pairs in field order. | |
| func (k Key) String() string { | |
| return k.string(true) | |
| } | |
| // StringValues returns Key as a space-separated sequence of | |
| // values in field order. | |
| func (k Key) StringValues() string { | |
| return k.string(false) | |
| } | |
| func (k Key) string(keys bool) string { | |
| if k.IsZero() { | |
| return "<zero>" | |
| } | |
| buf := new(strings.Builder) | |
| for _, field := range k.k.proj.FlattenedFields() { | |
| if field.idx >= len(k.k.vals) { | |
| continue | |
| } | |
| val := k.k.vals[field.idx] | |
| if val == "" { | |
| continue | |
| } | |
| if buf.Len() > 0 { | |
| buf.WriteByte(' ') | |
| } | |
| if keys { | |
| buf.WriteString(field.Name) | |
| buf.WriteByte(':') | |
| } | |
| buf.WriteString(val) | |
| } | |
| return buf.String() | |
| } | |
| // commonProjection returns the Projection that all Keys have, or panics if any | |
| // Key has a different Projection. It returns nil if len(keys) == 0. | |
| func commonProjection(keys []Key) *Projection { | |
| if len(keys) == 0 { | |
| return nil | |
| } | |
| s := keys[0].Projection() | |
| for _, k := range keys[1:] { | |
| if k.Projection() != s { | |
| panic("Keys must all have the same Projection") | |
| } | |
| } | |
| return s | |
| } | |
| // keyNode is the internal heap-allocated object backing a Key. | |
| // This allows Key itself to be a value type whose equality is | |
| // determined by the pointer equality of the underlying keyNode. | |
| type keyNode struct { | |
| proj *Projection | |
| // vals are the values in this Key, indexed by fieldInternal.idx. Trailing | |
| // ""s are always trimmed. | |
| // | |
| // Notably, this is *not* in the order of the flattened schema. This is | |
| // because fields can be added in the middle of a schema on-the-fly, and we | |
| // need to not invalidate existing Keys. | |
| vals []string | |
| } | |
| func (n *keyNode) equalRow(row []string) bool { | |
| if len(n.vals) != len(row) { | |
| return false | |
| } | |
| for i, v := range n.vals { | |
| if row[i] != v { | |
| return false | |
| } | |
| } | |
| return true | |
| } |