// 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 cmerge

// package cmerge provides a few small utility APIs for helping
// with merging of counter data for a given function.

import (
	
	
	
)

type ModeMergePolicy uint8

const (
	ModeMergeStrict ModeMergePolicy = iota
	ModeMergeRelaxed
)

// Merger provides state and methods to help manage the process of
// merging together coverage counter data for a given function, for
// tools that need to implicitly merge counter as they read multiple
// coverage counter data files.
type Merger struct {
	cmode    coverage.CounterMode
	cgran    coverage.CounterGranularity
	policy   ModeMergePolicy
	overflow bool
}

func ( *Merger) ( ModeMergePolicy) {
	.policy = 
}

// MergeCounters takes the counter values in 'src' and merges them
// into 'dst' according to the correct counter mode.
func ( *Merger) (,  []uint32) (error, bool) {
	if len() != len() {
		return fmt.Errorf("merging counters: len(dst)=%d len(src)=%d", len(), len()), false
	}
	if .cmode == coverage.CtrModeSet {
		for  := 0;  < len(); ++ {
			if [] != 0 {
				[] = 1
			}
		}
	} else {
		for  := 0;  < len(); ++ {
			[] = .SaturatingAdd([], [])
		}
	}
	 := .overflow
	.overflow = false
	return nil, 
}

// Saturating add does a saturating addition of 'dst' and 'src',
// returning added value or math.MaxUint32 if there is an overflow.
// Overflows are recorded in case the client needs to track them.
func ( *Merger) (,  uint32) uint32 {
	,  := SaturatingAdd(, )
	if  {
		.overflow = true
	}
	return 
}

// Saturating add does a saturating addition of 'dst' and 'src',
// returning added value or math.MaxUint32 plus an overflow flag.
func (,  uint32) (uint32, bool) {
	,  := uint64(), uint64()
	 :=  + 
	 := false
	if uint64(uint32()) !=  {
		 = true
		 = math.MaxUint32
	}
	return uint32(), 
}

// SetModeAndGranularity records the counter mode and granularity for
// the current merge. In the specific case of merging across coverage
// data files from different binaries, where we're combining data from
// more than one meta-data file, we need to check for and resolve
// mode/granularity clashes.
func ( *Merger) ( string,  coverage.CounterMode,  coverage.CounterGranularity) error {
	if .cmode == coverage.CtrModeInvalid {
		// Set merger mode based on what we're seeing here.
		.cmode = 
		.cgran = 
	} else {
		// Granularity clashes are always errors.
		if .cgran !=  {
			return fmt.Errorf("counter granularity clash while reading meta-data file %s: previous file had %s, new file has %s", , .cgran.String(), .String())
		}
		// Mode clashes are treated as errors if we're using the
		// default strict policy.
		if .cmode !=  {
			if .policy == ModeMergeStrict {
				return fmt.Errorf("counter mode clash while reading meta-data file %s: previous file had %s, new file has %s", , .cmode.String(), .String())
			}
			// In the case of a relaxed mode merge policy, upgrade
			// mode if needed.
			if .cmode <  {
				.cmode = 
			}
		}
	}
	return nil
}

func ( *Merger) () {
	.cmode = coverage.CtrModeInvalid
	.cgran = coverage.CtrGranularityInvalid
	.overflow = false
}

func ( *Merger) () coverage.CounterMode {
	return .cmode
}

func ( *Merger) () coverage.CounterGranularity {
	return .cgran
}