/*
 * SPDX-FileCopyrightText: © 2017-2025 Istari Digital, Inc.
 * SPDX-License-Identifier: Apache-2.0
 */

package badger

import (
	
	
	
	
	

	
	
)

// discardStats keeps track of the amount of data that could be discarded for
// a given logfile.
type discardStats struct {
	sync.Mutex

	*z.MmapFile
	opt           Options
	nextEmptySlot int
}

const discardFname string = "DISCARD"

func ( Options) (*discardStats, error) {
	 := filepath.Join(.ValueDir, discardFname)

	// 1MB file can store 65.536 discard entries. Each entry is 16 bytes.
	,  := z.OpenMmapFile(, os.O_CREATE|os.O_RDWR, 1<<20)
	 := &discardStats{
		MmapFile: ,
		opt:      ,
	}
	if  == z.NewFile {
		// We don't need to zero out the entire 1MB.
		.zeroOut()

	} else if  != nil {
		return nil, y.Wrapf(, "while opening file: %s\n", discardFname)
	}

	for  := 0;  < .maxSlot(); ++ {
		if .get(16*) == 0 {
			.nextEmptySlot = 
			break
		}
	}
	sort.Sort()
	.Infof("Discard stats nextEmptySlot: %d\n", .nextEmptySlot)
	return , nil
}

func ( *discardStats) () int {
	return .nextEmptySlot
}
func ( *discardStats) (,  int) bool {
	return .get(16*) < .get(16*)
}
func ( *discardStats) (,  int) {
	 := .Data[16* : 16*+16]
	 := .Data[16* : 16*+16]
	var  [16]byte
	copy([:], )
	copy(, )
	copy(, [:])
}

// offset is not slot.
func ( *discardStats) ( int) uint64 {
	return binary.BigEndian.Uint64(.Data[ : +8])
}
func ( *discardStats) ( int,  uint64) {
	binary.BigEndian.PutUint64(.Data[:+8], )
}

// zeroOut would zero out the next slot.
func ( *discardStats) () {
	.set(.nextEmptySlot*16, 0)
	.set(.nextEmptySlot*16+8, 0)
}

func ( *discardStats) () int {
	return len(.Data) / 16
}

// Update would update the discard stats for the given file id. If discard is
// 0, it would return the current value of discard for the file. If discard is
// < 0, it would set the current value of discard to zero for the file.
func ( *discardStats) ( uint32,  int64) int64 {
	 := uint64()
	.Lock()
	defer .Unlock()

	 := sort.Search(.nextEmptySlot, func( int) bool {
		return .get(*16) >= 
	})
	if  < .nextEmptySlot && .get(*16) ==  {
		 := *16 + 8
		 := .get()
		if  == 0 {
			return int64()
		}
		if  < 0 {
			.set(, 0)
			return 0
		}
		.set(, +uint64())
		return int64( + uint64())
	}
	if  <= 0 {
		// No need to add a new entry.
		return 0
	}

	// Could not find the fid. Add the entry.
	 = .nextEmptySlot
	.set(*16, )
	.set(*16+8, uint64())

	// Move to next slot.
	.nextEmptySlot++
	for .nextEmptySlot >= .maxSlot() {
		y.Check(.Truncate(2 * int64(len(.Data))))
	}
	.zeroOut()

	sort.Sort()
	return 
}

func ( *discardStats) ( func(,  uint64)) {
	for  := 0;  < .nextEmptySlot; ++ {
		 := 16 * 
		(.get(), .get(+8))
	}
}

// MaxDiscard returns the file id with maximum discard bytes.
func ( *discardStats) () (uint32, int64) {
	.Lock()
	defer .Unlock()

	var ,  uint64
	.Iterate(func(,  uint64) {
		if  <  {
			 = 
			 = 
		}
	})
	return uint32(), int64()
}