github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/internal/coverage/cmerge/merge.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 cmerge 6 7 // package cmerge provides a few small utility APIs for helping 8 // with merging of counter data for a given function. 9 10 import ( 11 "fmt" 12 "internal/coverage" 13 "math" 14 ) 15 16 // Merger provides state and methods to help manage the process of 17 // merging together coverage counter data for a given function, for 18 // tools that need to implicitly merge counter as they read multiple 19 // coverage counter data files. 20 type Merger struct { 21 cmode coverage.CounterMode 22 cgran coverage.CounterGranularity 23 overflow bool 24 } 25 26 // MergeCounters takes the counter values in 'src' and merges them 27 // into 'dst' according to the correct counter mode. 28 func (m *Merger) MergeCounters(dst, src []uint32) (error, bool) { 29 if len(src) != len(dst) { 30 return fmt.Errorf("merging counters: len(dst)=%d len(src)=%d", len(dst), len(src)), false 31 } 32 if m.cmode == coverage.CtrModeSet { 33 for i := 0; i < len(src); i++ { 34 if src[i] != 0 { 35 dst[i] = 1 36 } 37 } 38 } else { 39 for i := 0; i < len(src); i++ { 40 dst[i] = m.SaturatingAdd(dst[i], src[i]) 41 } 42 } 43 ovf := m.overflow 44 m.overflow = false 45 return nil, ovf 46 } 47 48 // Saturating add does a saturating addition of 'dst' and 'src', 49 // returning added value or math.MaxUint32 if there is an overflow. 50 // Overflows are recorded in case the client needs to track them. 51 func (m *Merger) SaturatingAdd(dst, src uint32) uint32 { 52 result, overflow := SaturatingAdd(dst, src) 53 if overflow { 54 m.overflow = true 55 } 56 return result 57 } 58 59 // Saturating add does a saturing addition of 'dst' and 'src', 60 // returning added value or math.MaxUint32 plus an overflow flag. 61 func SaturatingAdd(dst, src uint32) (uint32, bool) { 62 d, s := uint64(dst), uint64(src) 63 sum := d + s 64 overflow := false 65 if uint64(uint32(sum)) != sum { 66 overflow = true 67 sum = math.MaxUint32 68 } 69 return uint32(sum), overflow 70 } 71 72 // SetModeAndGranularity records the counter mode and granularity for 73 // the current merge. In the specific case of merging across coverage 74 // data files from different binaries, where we're combining data from 75 // more than one meta-data file, we need to check for mode/granularity 76 // clashes. 77 func (cm *Merger) SetModeAndGranularity(mdf string, cmode coverage.CounterMode, cgran coverage.CounterGranularity) error { 78 // Collect counter mode and granularity so as to detect clashes. 79 if cm.cmode != coverage.CtrModeInvalid { 80 if cm.cmode != cmode { 81 return fmt.Errorf("counter mode clash while reading meta-data file %s: previous file had %s, new file has %s", mdf, cm.cmode.String(), cmode.String()) 82 } 83 if cm.cgran != cgran { 84 return fmt.Errorf("counter granularity clash while reading meta-data file %s: previous file had %s, new file has %s", mdf, cm.cgran.String(), cgran.String()) 85 } 86 } 87 cm.cmode = cmode 88 cm.cgran = cgran 89 return nil 90 } 91 92 func (cm *Merger) ResetModeAndGranularity() { 93 cm.cmode = coverage.CtrModeInvalid 94 cm.cgran = coverage.CtrGranularityInvalid 95 cm.overflow = false 96 } 97 98 func (cm *Merger) Mode() coverage.CounterMode { 99 return cm.cmode 100 } 101 102 func (cm *Merger) Granularity() coverage.CounterGranularity { 103 return cm.cgran 104 }