//go: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.HostProc("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)

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

	.Cached += .SReclaimable

	if ! {
		if  &&  &&  {
			.Available = calcuateAvailVmem(, )
		} 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.HostProc("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
}

// calcuateAvailVmem 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 calcuateAvailVmem( *VirtualMemoryStat,  *VirtualMemoryExStat) uint64 {
	var  uint64

	 := common.HostProc("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()))

	return 
}