func (m *Map) Type() MapType {
	return m.typ
}
Type returns the underlying type of the map.
package ebpf
import (
	"errors"
	"fmt"
	"io"
	"path/filepath"
	"reflect"
	"strings"
	"github.com/cilium/ebpf/internal"
	"github.com/cilium/ebpf/internal/btf"
	"github.com/cilium/ebpf/internal/unix"
)
var (
	ErrKeyNotExist      = errors.New("key does not exist")
	ErrKeyExist         = errors.New("key already exists")
	ErrIterationAborted = errors.New("iteration aborted")
	ErrMapIncompatible  = errors.New("map's spec is incompatible with pinned map")
)
type MapOptions struct {
	PinPath        string
	LoadPinOptions LoadPinOptions
}
type MapID uint32
type MapSpec struct {
	Name       string
	Type       MapType
	KeySize    uint32
	ValueSize  uint32
	MaxEntries uint32
	Flags uint32
	Pinning PinType
	NumaNode uint32
	Contents []MapKV
	Freeze bool
	InnerMap *MapSpec
	BTF *btf.Map
}
func (ms *MapSpec) String() string {
	return fmt.Sprintf("%s(keySize=%d, valueSize=%d, maxEntries=%d, flags=%d)", ms.Type, ms.KeySize, ms.ValueSize, ms.MaxEntries, ms.Flags)
}
func (ms *MapSpec) Copy() *MapSpec {
	if ms == nil {
		return nil
	}
	cpy := *ms
	cpy.Contents = make([]MapKV, len(ms.Contents))
	copy(cpy.Contents, ms.Contents)
	cpy.InnerMap = ms.InnerMap.Copy()
	return &cpy
}
func (ms *MapSpec) clampPerfEventArraySize() error {
	if ms.Type != PerfEventArray {
		return nil
	}
	n, err := internal.PossibleCPUs()
	if err != nil {
		return fmt.Errorf("perf event array: %w", err)
	}
	if n := uint32(n); ms.MaxEntries > n {
		ms.MaxEntries = n
	}
	return nil
}
type MapKV struct {
	Key   interface{}
	Value interface{}
}
func (ms *MapSpec) checkCompatibility(m *Map) error {
	switch {
	case m.typ != ms.Type:
		return fmt.Errorf("expected type %v, got %v: %w", ms.Type, m.typ, ErrMapIncompatible)
	case m.keySize != ms.KeySize:
		return fmt.Errorf("expected key size %v, got %v: %w", ms.KeySize, m.keySize, ErrMapIncompatible)
	case m.valueSize != ms.ValueSize:
		return fmt.Errorf("expected value size %v, got %v: %w", ms.ValueSize, m.valueSize, ErrMapIncompatible)
	case m.maxEntries != ms.MaxEntries:
		return fmt.Errorf("expected max entries %v, got %v: %w", ms.MaxEntries, m.maxEntries, ErrMapIncompatible)
	case m.flags != ms.Flags:
		return fmt.Errorf("expected flags %v, got %v: %w", ms.Flags, m.flags, ErrMapIncompatible)
	}
	return nil
}
type Map struct {
	name       string
	fd         *internal.FD
	typ        MapType
	keySize    uint32
	valueSize  uint32
	maxEntries uint32
	flags      uint32
	pinnedPath string
	fullValueSize int
}
func NewMapFromFD(fd int) (*Map, error) {
	if fd < 0 {
		return nil, errors.New("invalid fd")
	}
	return newMapFromFD(internal.NewFD(uint32(fd)))
}
func newMapFromFD(fd *internal.FD) (*Map, error) {
	info, err := newMapInfoFromFd(fd)
	if err != nil {
		fd.Close()
		return nil, fmt.Errorf("get map info: %s", err)
	}
	return newMap(fd, info.Name, info.Type, info.KeySize, info.ValueSize, info.MaxEntries, info.Flags)
}
func NewMap(spec *MapSpec) (*Map, error) {
	return NewMapWithOptions(spec, MapOptions{})
}
func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
	handles := newHandleCache()
	defer handles.close()
	return newMapWithOptions(spec, opts, handles)
}
func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *Map, err error) {
	closeOnError := func(c io.Closer) {
		if err != nil {
			c.Close()
		}
	}
	switch spec.Pinning {
	case PinByName:
		if spec.Name == "" || opts.PinPath == "" {
			return nil, fmt.Errorf("pin by name: missing Name or PinPath")
		}
		path := filepath.Join(opts.PinPath, spec.Name)
		m, err := LoadPinnedMap(path, &opts.LoadPinOptions)
		if errors.Is(err, unix.ENOENT) {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("load pinned map: %w", err)
		}
		defer closeOnError(m)
		if err := spec.checkCompatibility(m); err != nil {
			return nil, fmt.Errorf("use pinned map %s: %w", spec.Name, err)
		}
		return m, nil
	case PinNone:
	default:
		return nil, fmt.Errorf("pin type %d: %w", int(spec.Pinning), ErrNotSupported)
	}
	var innerFd *internal.FD
	if spec.Type == ArrayOfMaps || spec.Type == HashOfMaps {
		if spec.InnerMap == nil {
			return nil, fmt.Errorf("%s requires InnerMap", spec.Type)
		}
		if spec.InnerMap.Pinning != PinNone {
			return nil, errors.New("inner maps cannot be pinned")
		}
		template, err := createMap(spec.InnerMap, nil, opts, handles)
		if err != nil {
			return nil, err
		}
		defer template.Close()
		innerFd = template.fd
	}
	m, err := createMap(spec, innerFd, opts, handles)
	if err != nil {
		return nil, err
	}
	defer closeOnError(m)
	if spec.Pinning == PinByName {
		path := filepath.Join(opts.PinPath, spec.Name)
		if err := m.Pin(path); err != nil {
			return nil, fmt.Errorf("pin map: %s", err)
		}
	}
	return m, nil
}
func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) {
	closeOnError := func(closer io.Closer) {
		if err != nil {
			closer.Close()
		}
	}
	spec = spec.Copy()
	switch spec.Type {
	case ArrayOfMaps:
		fallthrough
	case HashOfMaps:
		if err := haveNestedMaps(); err != nil {
			return nil, err
		}
		if spec.ValueSize != 0 && spec.ValueSize != 4 {
			return nil, errors.New("ValueSize must be zero or four for map of map")
		}
		spec.ValueSize = 4
	case PerfEventArray:
		if spec.KeySize != 0 && spec.KeySize != 4 {
			return nil, errors.New("KeySize must be zero or four for perf event array")
		}
		spec.KeySize = 4
		if spec.ValueSize != 0 && spec.ValueSize != 4 {
			return nil, errors.New("ValueSize must be zero or four for perf event array")
		}
		spec.ValueSize = 4
		if spec.MaxEntries == 0 {
			n, err := internal.PossibleCPUs()
			if err != nil {
				return nil, fmt.Errorf("perf event array: %w", err)
			}
			spec.MaxEntries = uint32(n)
		}
	}
	if spec.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze {
		if err := haveMapMutabilityModifiers(); err != nil {
			return nil, fmt.Errorf("map create: %w", err)
		}
	}
	if spec.Flags&unix.BPF_F_MMAPABLE > 0 {
		if err := haveMmapableMaps(); err != nil {
			return nil, fmt.Errorf("map create: %w", err)
		}
	}
	if spec.Flags&unix.BPF_F_INNER_MAP > 0 {
		if err := haveInnerMaps(); err != nil {
			return nil, fmt.Errorf("map create: %w", err)
		}
	}
	attr := internal.BPFMapCreateAttr{
		MapType:    uint32(spec.Type),
		KeySize:    spec.KeySize,
		ValueSize:  spec.ValueSize,
		MaxEntries: spec.MaxEntries,
		Flags:      spec.Flags,
		NumaNode:   spec.NumaNode,
	}
	if inner != nil {
		var err error
		attr.InnerMapFd, err = inner.Value()
		if err != nil {
			return nil, fmt.Errorf("map create: %w", err)
		}
	}
	if haveObjName() == nil {
		attr.MapName = internal.NewBPFObjName(spec.Name)
	}
	var btfDisabled bool
	if spec.BTF != nil {
		handle, err := handles.btfHandle(btf.MapSpec(spec.BTF))
		btfDisabled = errors.Is(err, btf.ErrNotSupported)
		if err != nil && !btfDisabled {
			return nil, fmt.Errorf("load BTF: %w", err)
		}
		if handle != nil {
			attr.BTFFd = uint32(handle.FD())
			attr.BTFKeyTypeID = uint32(btf.MapKey(spec.BTF).ID())
			attr.BTFValueTypeID = uint32(btf.MapValue(spec.BTF).ID())
		}
	}
	fd, err := internal.BPFMapCreate(&attr)
	if err != nil {
		if errors.Is(err, unix.EPERM) {
			return nil, fmt.Errorf("map create: RLIMIT_MEMLOCK may be too low: %w", err)
		}
		if btfDisabled {
			return nil, fmt.Errorf("map create without BTF: %w", err)
		}
		return nil, fmt.Errorf("map create: %w", err)
	}
	defer closeOnError(fd)
	m, err := newMap(fd, spec.Name, spec.Type, spec.KeySize, spec.ValueSize, spec.MaxEntries, spec.Flags)
	if err != nil {
		return nil, fmt.Errorf("map create: %w", err)
	}
	if err := m.populate(spec.Contents); err != nil {
		return nil, fmt.Errorf("map create: can't set initial contents: %w", err)
	}
	if spec.Freeze {
		if err := m.Freeze(); err != nil {
			return nil, fmt.Errorf("can't freeze map: %w", err)
		}
	}
	return m, nil
}
func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) {
	m := &Map{
		name,
		fd,
		typ,
		keySize,
		valueSize,
		maxEntries,
		flags,
		"",
		int(valueSize),
	}
	if !typ.hasPerCPUValue() {
		return m, nil
	}
	possibleCPUs, err := internal.PossibleCPUs()
	if err != nil {
		return nil, err
	}
	m.fullValueSize = align(int(valueSize), 8) * possibleCPUs
	return m, nil
}
func (m *Map) String() string {
	if m.name != "" {
		return fmt.Sprintf("%s(%s)#%v", m.typ, m.name, m.fd)
	}
	return fmt.Sprintf("%s#%v", m.typ, m.fd)
}