github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/bpf/input_bytes.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package bpf
    16  
    17  import (
    18  	"encoding/binary"
    19  )
    20  
    21  // Input represents a source of input data for a BPF program. (BPF
    22  // documentation sometimes refers to the input data as the "packet" due to its
    23  // origins as a packet processing DSL.)
    24  // Unaligned loads are supported.
    25  type Input []byte
    26  
    27  // These type definitions must have different GC shapes to ensure that
    28  // the Go compiler generates distinct code paths for them.
    29  // These do not have anything to do with the bit sizes of the loads
    30  // later on; all that matters is that these types have distinct sizes
    31  // from one another.
    32  type (
    33  	// BigEndian uses big-endian byte ordering.
    34  	BigEndian uint8
    35  
    36  	// LittleEndian uses little-endian byte ordering.
    37  	LittleEndian uint16
    38  
    39  	// NativeEndian uses native byte ordering.
    40  	NativeEndian uint32
    41  )
    42  
    43  // Endianness represents a byte order.
    44  type Endianness interface {
    45  	BigEndian | LittleEndian | NativeEndian
    46  }
    47  
    48  // load32 loads a 32-bit value.
    49  //
    50  //go:nosplit
    51  func load32[endian Endianness](in Input, off uint32) (uint32, bool) {
    52  	if uint64(off)+4 > uint64(len(in)) {
    53  		return 0, false
    54  	}
    55  	// Casting to any is needed here to avoid a compilation error:
    56  	// https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#why-not-permit-type-assertions-on-values-whose-type-is-a-type-parameter
    57  	var e endian
    58  	switch any(e).(type) {
    59  	case BigEndian:
    60  		return binary.BigEndian.Uint32(in[int(off):]), true
    61  	case LittleEndian:
    62  		return binary.LittleEndian.Uint32(in[int(off):]), true
    63  	case NativeEndian:
    64  		return binary.NativeEndian.Uint32(in[int(off):]), true
    65  	default:
    66  		panic("unreachable")
    67  	}
    68  }
    69  
    70  // load16 loads a 16-bit value.
    71  //
    72  //go:nosplit
    73  func load16[endian Endianness](in Input, off uint32) (uint16, bool) {
    74  	if uint64(off)+2 > uint64(len(in)) {
    75  		return 0, false
    76  	}
    77  	// Casting to any is needed here to avoid a compilation error:
    78  	// https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#why-not-permit-type-assertions-on-values-whose-type-is-a-type-parameter
    79  	var e endian
    80  	switch any(e).(type) {
    81  	case BigEndian:
    82  		return binary.BigEndian.Uint16(in[int(off):]), true
    83  	case LittleEndian:
    84  		return binary.LittleEndian.Uint16(in[int(off):]), true
    85  	case NativeEndian:
    86  		return binary.NativeEndian.Uint16(in[int(off):]), true
    87  	default:
    88  		panic("unreachable")
    89  	}
    90  }
    91  
    92  // load8 loads a single byte.
    93  //
    94  //go:nosplit
    95  func load8(in Input, off uint32) (uint8, bool) {
    96  	if uint64(off)+1 > uint64(len(in)) {
    97  		return 0, false
    98  	}
    99  	return in[int(off)], true
   100  }