// Copyright 2019 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package procfs

import (
	
	
	
	
	
	

	
)

// For the proc file format details,
// See:
// * Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2343
// * Linux 2.6.39 https://elixir.bootlin.com/linux/v2.6.39/source/net/core/dev.c#L4086
// * Linux 4.18 https://elixir.bootlin.com/linux/v4.18/source/net/core/net-procfs.c#L162
// * Linux 5.14 https://elixir.bootlin.com/linux/v5.14/source/net/core/net-procfs.c#L169

// SoftnetStat contains a single row of data from /proc/net/softnet_stat.
type SoftnetStat struct {
	// Number of processed packets.
	Processed uint32
	// Number of dropped packets.
	Dropped uint32
	// Number of times processing packets ran out of quota.
	TimeSqueezed uint32
	// Number of collision occur while obtaining device lock while transmitting.
	CPUCollision uint32
	// Number of times cpu woken up received_rps.
	ReceivedRps uint32
	// number of times flow limit has been reached.
	FlowLimitCount uint32
	// Softnet backlog status.
	SoftnetBacklogLen uint32
	// CPU id owning this softnet_data.
	Index uint32
	// softnet_data's Width.
	Width int
}

var softNetProcFile = "net/softnet_stat"

// NetSoftnetStat reads data from /proc/net/softnet_stat.
func ( FS) () ([]SoftnetStat, error) {
	,  := util.ReadFileNoStat(.proc.Path(softNetProcFile))
	if  != nil {
		return nil, 
	}

	,  := parseSoftnet(bytes.NewReader())
	if  != nil {
		return nil, fmt.Errorf("%w: /proc/net/softnet_stat: %w", ErrFileParse, )
	}

	return , nil
}

func parseSoftnet( io.Reader) ([]SoftnetStat, error) {
	const  = 9

	 := bufio.NewScanner()

	var  []SoftnetStat
	 := 0
	for .Scan() {
		 := strings.Fields(.Text())
		 := len()
		 := SoftnetStat{}

		if  <  {
			return nil, fmt.Errorf("%w: detected %d columns, but expected at least %d", ErrFileParse, , )
		}

		// Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2347
		if  >=  {
			,  := parseHexUint32s([0:9])
			if  != nil {
				return nil, 
			}

			.Processed = [0]
			.Dropped = [1]
			.TimeSqueezed = [2]
			.CPUCollision = [8]
		}

		// Linux 2.6.39 https://elixir.bootlin.com/linux/v2.6.39/source/net/core/dev.c#L4086
		if  >= 10 {
			,  := parseHexUint32s([9:10])
			if  != nil {
				return nil, 
			}

			.ReceivedRps = [0]
		}

		// Linux 4.18 https://elixir.bootlin.com/linux/v4.18/source/net/core/net-procfs.c#L162
		if  >= 11 {
			,  := parseHexUint32s([10:11])
			if  != nil {
				return nil, 
			}

			.FlowLimitCount = [0]
		}

		// Linux 5.14 https://elixir.bootlin.com/linux/v5.14/source/net/core/net-procfs.c#L169
		if  >= 13 {
			,  := parseHexUint32s([11:13])
			if  != nil {
				return nil, 
			}

			.SoftnetBacklogLen = [0]
			.Index = [1]
		} else {
			// For older kernels, create the Index based on the scan line number.
			.Index = uint32()
		}
		.Width = 
		 = append(, )
		++
	}

	return , nil
}

func parseHexUint32s( []string) ([]uint32, error) {
	 := make([]uint32, 0, len())
	for ,  := range  {
		,  := strconv.ParseUint(, 16, 32)
		if  != nil {
			return nil, 
		}

		 = append(, uint32())
	}

	return , nil
}