github.com/jgbaldwinbrown/perf@v0.1.1/benchproc/key.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package benchproc
     6  
     7  import "strings"
     8  
     9  // A Key is an immutable tuple mapping from Fields to strings whose
    10  // structure is given by a Projection. Two Keys are == if they come
    11  // from the same Projection and have identical values.
    12  type Key struct {
    13  	k *keyNode
    14  }
    15  
    16  // IsZero reports whether k is a zeroed Key with no projection and no fields.
    17  func (k Key) IsZero() bool {
    18  	return k.k == nil
    19  }
    20  
    21  // Get returns the value of Field f in this Key.
    22  //
    23  // It panics if Field f does not come from the same Projection as the
    24  // Key or if f is a tuple Field.
    25  func (k Key) Get(f *Field) string {
    26  	if k.IsZero() {
    27  		panic("zero Key has no fields")
    28  	}
    29  	if k.k.proj != f.proj {
    30  		panic("Key and Field have different Projections")
    31  	}
    32  	if f.IsTuple {
    33  		panic(f.Name + " is a tuple field")
    34  	}
    35  	idx := f.idx
    36  	if idx >= len(k.k.vals) {
    37  		return ""
    38  	}
    39  	return k.k.vals[idx]
    40  }
    41  
    42  // Projection returns the Projection describing Key k.
    43  func (k Key) Projection() *Projection {
    44  	if k.IsZero() {
    45  		return nil
    46  	}
    47  	return k.k.proj
    48  }
    49  
    50  // String returns Key as a space-separated sequence of key:value
    51  // pairs in field order.
    52  func (k Key) String() string {
    53  	return k.string(true)
    54  }
    55  
    56  // StringValues returns Key as a space-separated sequences of
    57  // values in field order.
    58  func (k Key) StringValues() string {
    59  	return k.string(false)
    60  }
    61  
    62  func (k Key) string(keys bool) string {
    63  	if k.IsZero() {
    64  		return "<zero>"
    65  	}
    66  	buf := new(strings.Builder)
    67  	for _, field := range k.k.proj.FlattenedFields() {
    68  		if field.idx >= len(k.k.vals) {
    69  			continue
    70  		}
    71  		val := k.k.vals[field.idx]
    72  		if val == "" {
    73  			continue
    74  		}
    75  		if buf.Len() > 0 {
    76  			buf.WriteByte(' ')
    77  		}
    78  		if keys {
    79  			buf.WriteString(field.Name)
    80  			buf.WriteByte(':')
    81  		}
    82  		buf.WriteString(val)
    83  	}
    84  	return buf.String()
    85  }
    86  
    87  // commonProjection returns the Projection that all Keys have, or panics if any
    88  // Key has a different Projection. It returns nil if len(keys) == 0.
    89  func commonProjection(keys []Key) *Projection {
    90  	if len(keys) == 0 {
    91  		return nil
    92  	}
    93  	s := keys[0].Projection()
    94  	for _, k := range keys[1:] {
    95  		if k.Projection() != s {
    96  			panic("Keys must all have the same Projection")
    97  		}
    98  	}
    99  	return s
   100  }
   101  
   102  // keyNode is the internal heap-allocated object backing a Key.
   103  // This allows Key itself to be a value type whose equality is
   104  // determined by the pointer equality of the underlying keyNode.
   105  type keyNode struct {
   106  	proj *Projection
   107  	// vals are the values in this Key, indexed by fieldInternal.idx. Trailing
   108  	// ""s are always trimmed.
   109  	//
   110  	// Notably, this is *not* in the order of the flattened schema. This is
   111  	// because fields can be added in the middle of a schema on-the-fly, and we
   112  	// need to not invalidate existing Keys.
   113  	vals []string
   114  }
   115  
   116  func (n *keyNode) equalRow(row []string) bool {
   117  	if len(n.vals) != len(row) {
   118  		return false
   119  	}
   120  	for i, v := range n.vals {
   121  		if row[i] != v {
   122  			return false
   123  		}
   124  	}
   125  	return true
   126  }