//go:build linux
// +build linux

package mem

import (
	
	
	
	
	
	
	
	
	

	

	
)

type VirtualMemoryExStat struct {
	ActiveFile   uint64 `json:"activefile"`
	InactiveFile uint64 `json:"inactivefile"`
	ActiveAnon   uint64 `json:"activeanon"`
	InactiveAnon uint64 `json:"inactiveanon"`
	Unevictable  uint64 `json:"unevictable"`
}

func ( VirtualMemoryExStat) () string {
	,  := json.Marshal()
	return string()
}

func () (*VirtualMemoryStat, error) {
	return VirtualMemoryWithContext(context.Background())
}

func ( context.Context) (*VirtualMemoryStat, error) {
	, ,  := fillFromMeminfoWithContext()
	if  != nil {
		return nil, 
	}
	return , nil
}

func () (*VirtualMemoryExStat, error) {
	return VirtualMemoryExWithContext(context.Background())
}

func ( context.Context) (*VirtualMemoryExStat, error) {
	, ,  := fillFromMeminfoWithContext()
	if  != nil {
		return nil, 
	}
	return , nil
}

func fillFromMeminfoWithContext( context.Context) (*VirtualMemoryStat, *VirtualMemoryExStat, error) {
	 := common.HostProcWithContext(, "meminfo")
	,  := common.ReadLines()

	// flag if MemAvailable is in /proc/meminfo (kernel 3.14+)
	 := false
	 := false   // "Active(file)" not available: 2.6.28 / Dec 2008
	 := false // "Inactive(file)" not available: 2.6.28 / Dec 2008
	 := false // "Sreclaimable:" not available: 2.6.19 / Nov 2006

	 := &VirtualMemoryStat{}
	 := &VirtualMemoryExStat{}

	for ,  := range  {
		 := strings.Split(, ":")
		if len() != 2 {
			continue
		}
		 := strings.TrimSpace([0])
		 := strings.TrimSpace([1])
		 = strings.Replace(, " kB", "", -1)

		switch  {
		case "MemTotal":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Total =  * 1024
		case "MemFree":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Free =  * 1024
		case "MemAvailable":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			 = true
			.Available =  * 1024
		case "Buffers":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Buffers =  * 1024
		case "Cached":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Cached =  * 1024
		case "Active":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Active =  * 1024
		case "Inactive":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Inactive =  * 1024
		case "Active(anon)":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.ActiveAnon =  * 1024
		case "Inactive(anon)":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.InactiveAnon =  * 1024
		case "Active(file)":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			 = true
			.ActiveFile =  * 1024
		case "Inactive(file)":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			 = true
			.InactiveFile =  * 1024
		case "Unevictable":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Unevictable =  * 1024
		case "Writeback":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.WriteBack =  * 1024
		case "WritebackTmp":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.WriteBackTmp =  * 1024
		case "Dirty":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Dirty =  * 1024
		case "Shmem":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Shared =  * 1024
		case "Slab":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Slab =  * 1024
		case "SReclaimable":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			 = true
			.Sreclaimable =  * 1024
		case "SUnreclaim":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Sunreclaim =  * 1024
		case "PageTables":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.PageTables =  * 1024
		case "SwapCached":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.SwapCached =  * 1024
		case "CommitLimit":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.CommitLimit =  * 1024
		case "Committed_AS":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.CommittedAS =  * 1024
		case "HighTotal":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.HighTotal =  * 1024
		case "HighFree":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.HighFree =  * 1024
		case "LowTotal":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.LowTotal =  * 1024
		case "LowFree":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.LowFree =  * 1024
		case "SwapTotal":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.SwapTotal =  * 1024
		case "SwapFree":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.SwapFree =  * 1024
		case "Mapped":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.Mapped =  * 1024
		case "VmallocTotal":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.VmallocTotal =  * 1024
		case "VmallocUsed":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.VmallocUsed =  * 1024
		case "VmallocChunk":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.VmallocChunk =  * 1024
		case "HugePages_Total":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.HugePagesTotal = 
		case "HugePages_Free":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.HugePagesFree = 
		case "HugePages_Rsvd":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.HugePagesRsvd = 
		case "HugePages_Surp":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.HugePagesSurp = 
		case "Hugepagesize":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.HugePageSize =  * 1024
		case "AnonHugePages":
			,  := strconv.ParseUint(, 10, 64)
			if  != nil {
				return , , 
			}
			.AnonHugePages =  * 1024
		}
	}

	.Cached += .Sreclaimable

	if ! {
		if  &&  &&  {
			.Available = calculateAvailVmem(, , )
		} else {
			.Available = .Cached + .Free
		}
	}

	.Used = .Total - .Free - .Buffers - .Cached
	.UsedPercent = float64(.Used) / float64(.Total) * 100.0

	return , , nil
}

func () (*SwapMemoryStat, error) {
	return SwapMemoryWithContext(context.Background())
}

func ( context.Context) (*SwapMemoryStat, error) {
	 := &unix.Sysinfo_t{}

	if  := unix.Sysinfo();  != nil {
		return nil, 
	}
	 := &SwapMemoryStat{
		Total: uint64(.Totalswap) * uint64(.Unit),
		Free:  uint64(.Freeswap) * uint64(.Unit),
	}
	.Used = .Total - .Free
	// check Infinity
	if .Total != 0 {
		.UsedPercent = float64(.Total-.Free) / float64(.Total) * 100.0
	} else {
		.UsedPercent = 0
	}
	 := common.HostProcWithContext(, "vmstat")
	,  := common.ReadLines()
	for ,  := range  {
		 := strings.Fields()
		if len() < 2 {
			continue
		}
		switch [0] {
		case "pswpin":
			,  := strconv.ParseUint([1], 10, 64)
			if  != nil {
				continue
			}
			.Sin =  * 4 * 1024
		case "pswpout":
			,  := strconv.ParseUint([1], 10, 64)
			if  != nil {
				continue
			}
			.Sout =  * 4 * 1024
		case "pgpgin":
			,  := strconv.ParseUint([1], 10, 64)
			if  != nil {
				continue
			}
			.PgIn =  * 4 * 1024
		case "pgpgout":
			,  := strconv.ParseUint([1], 10, 64)
			if  != nil {
				continue
			}
			.PgOut =  * 4 * 1024
		case "pgfault":
			,  := strconv.ParseUint([1], 10, 64)
			if  != nil {
				continue
			}
			.PgFault =  * 4 * 1024
		case "pgmajfault":
			,  := strconv.ParseUint([1], 10, 64)
			if  != nil {
				continue
			}
			.PgMajFault =  * 4 * 1024
		}
	}
	return , nil
}

// calculateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide
// "MemAvailable:" column. It reimplements an algorithm from the link below
// https://github.com/giampaolo/psutil/pull/890
func calculateAvailVmem( context.Context,  *VirtualMemoryStat,  *VirtualMemoryExStat) uint64 {
	var  uint64

	 := common.HostProcWithContext(, "zoneinfo")
	,  := common.ReadLines()
	if  != nil {
		return .Free + .Cached // fallback under kernel 2.6.13
	}

	 := uint64(os.Getpagesize())
	 = 0

	for ,  := range  {
		 := strings.Fields()

		if strings.HasPrefix([0], "low") {
			,  := strconv.ParseUint([1], 10, 64)
			if  != nil {
				 = 0
			}
			 += 
		}
	}

	 *= 

	 := .Free - 
	 := .ActiveFile + .InactiveFile
	 -= uint64(math.Min(float64(/2), float64()))
	 += 
	 += .Sreclaimable - uint64(math.Min(float64(.Sreclaimable/2.0), float64()))

	if  < 0 {
		 = 0
	}

	return 
}

const swapsFilename = "swaps"

// swaps file column indexes
const (
	nameCol = 0
	// typeCol     = 1
	totalCol = 2
	usedCol  = 3
	// priorityCol = 4
)

func () ([]*SwapDevice, error) {
	return SwapDevicesWithContext(context.Background())
}

func ( context.Context) ([]*SwapDevice, error) {
	 := common.HostProcWithContext(, swapsFilename)
	,  := os.Open()
	if  != nil {
		return nil, 
	}
	defer .Close()

	return parseSwapsFile(, )
}

func parseSwapsFile( context.Context,  io.Reader) ([]*SwapDevice, error) {
	 := common.HostProcWithContext(, swapsFilename)
	 := bufio.NewScanner()
	if !.Scan() {
		if  := .Err();  != nil {
			return nil, fmt.Errorf("couldn't read file %q: %w", , )
		}
		return nil, fmt.Errorf("unexpected end-of-file in %q", )

	}

	// Check header headerFields are as expected
	 := strings.Fields(.Text())
	if len() < usedCol {
		return nil, fmt.Errorf("couldn't parse %q: too few fields in header", )
	}
	if [nameCol] != "Filename" {
		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", , [nameCol], "Filename")
	}
	if [totalCol] != "Size" {
		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", , [totalCol], "Size")
	}
	if [usedCol] != "Used" {
		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", , [usedCol], "Used")
	}

	var  []*SwapDevice
	for .Scan() {
		 := strings.Fields(.Text())
		if len() < usedCol {
			return nil, fmt.Errorf("couldn't parse %q: too few fields", )
		}

		,  := strconv.ParseUint([totalCol], 10, 64)
		if  != nil {
			return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", , )
		}

		,  := strconv.ParseUint([usedCol], 10, 64)
		if  != nil {
			return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", , )
		}

		 = append(, &SwapDevice{
			Name:      [nameCol],
			UsedBytes:  * 1024,
			FreeBytes: ( - ) * 1024,
		})
	}

	if  := .Err();  != nil {
		return nil, fmt.Errorf("couldn't read file %q: %w", , )
	}

	return , nil
}