github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/hostarch/addr_range_seq_unsafe.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 hostarch
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"unsafe"
    21  
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/gohacks"
    23  )
    24  
    25  // An AddrRangeSeq represents a sequence of AddrRanges.
    26  //
    27  // AddrRangeSeqs are immutable and may be copied by value. The zero value of
    28  // AddrRangeSeq represents an empty sequence.
    29  //
    30  // An AddrRangeSeq may contain AddrRanges with a length of 0. This is necessary
    31  // since zero-length AddrRanges are significant to MM bounds checks.
    32  type AddrRangeSeq struct {
    33  	// If length is 0, then the AddrRangeSeq represents no AddrRanges.
    34  	// Invariants: data == 0; offset == 0; limit == 0.
    35  	//
    36  	// If length is 1, then the AddrRangeSeq represents the single
    37  	// AddrRange{offset, offset+limit}. Invariants: data == 0.
    38  	//
    39  	// Otherwise, length >= 2, and the AddrRangeSeq represents the `length`
    40  	// AddrRanges in the array of AddrRanges starting at address `data`,
    41  	// starting at `offset` bytes into the first AddrRange and limited to the
    42  	// following `limit` bytes. (AddrRanges after `limit` are still iterated,
    43  	// but are truncated to a length of 0.) Invariants: data != 0; offset <=
    44  	// data[0].Length(); limit > 0; offset+limit <= the combined length of all
    45  	// AddrRanges in the array.
    46  	data   unsafe.Pointer
    47  	length int
    48  	offset Addr
    49  	limit  Addr
    50  }
    51  
    52  // AddrRangeSeqOf returns an AddrRangeSeq representing the single AddrRange ar.
    53  func AddrRangeSeqOf(ar AddrRange) AddrRangeSeq {
    54  	return AddrRangeSeq{
    55  		length: 1,
    56  		offset: ar.Start,
    57  		limit:  ar.Length(),
    58  	}
    59  }
    60  
    61  // AddrRangeSeqFromSlice returns an AddrRangeSeq representing all AddrRanges in
    62  // slice.
    63  //
    64  // Whether the returned AddrRangeSeq shares memory with slice is unspecified;
    65  // clients should avoid mutating slices passed to AddrRangeSeqFromSlice.
    66  //
    67  // Preconditions: The combined length of all AddrRanges in slice <=
    68  // math.MaxInt64.
    69  func AddrRangeSeqFromSlice(slice []AddrRange) AddrRangeSeq {
    70  	var limit int64
    71  	for _, ar := range slice {
    72  		len64 := int64(ar.Length())
    73  		if len64 < 0 {
    74  			panic(fmt.Sprintf("Length of AddrRange %v overflows int64", ar))
    75  		}
    76  		sum := limit + len64
    77  		if sum < limit {
    78  			panic(fmt.Sprintf("Total length of AddrRanges %v overflows int64", slice))
    79  		}
    80  		limit = sum
    81  	}
    82  	return addrRangeSeqFromSliceLimited(slice, limit)
    83  }
    84  
    85  // Preconditions:
    86  //   - The combined length of all AddrRanges in slice <= limit.
    87  //   - limit >= 0.
    88  //   - If len(slice) != 0, then limit > 0.
    89  func addrRangeSeqFromSliceLimited(slice []AddrRange, limit int64) AddrRangeSeq {
    90  	switch len(slice) {
    91  	case 0:
    92  		return AddrRangeSeq{}
    93  	case 1:
    94  		return AddrRangeSeq{
    95  			length: 1,
    96  			offset: slice[0].Start,
    97  			limit:  Addr(limit),
    98  		}
    99  	default:
   100  		return AddrRangeSeq{
   101  			data:   unsafe.Pointer(&slice[0]),
   102  			length: len(slice),
   103  			limit:  Addr(limit),
   104  		}
   105  	}
   106  }
   107  
   108  // IsEmpty returns true if ars.NumRanges() == 0.
   109  //
   110  // Note that since AddrRangeSeq may contain AddrRanges with a length of zero,
   111  // an AddrRange representing 0 bytes (AddrRangeSeq.NumBytes() == 0) is not
   112  // necessarily empty.
   113  func (ars AddrRangeSeq) IsEmpty() bool {
   114  	return ars.length == 0
   115  }
   116  
   117  // NumRanges returns the number of AddrRanges in ars.
   118  func (ars AddrRangeSeq) NumRanges() int {
   119  	return ars.length
   120  }
   121  
   122  // NumBytes returns the number of bytes represented by ars.
   123  func (ars AddrRangeSeq) NumBytes() int64 {
   124  	return int64(ars.limit)
   125  }
   126  
   127  // Head returns the first AddrRange in ars.
   128  //
   129  // Preconditions: !ars.IsEmpty().
   130  func (ars AddrRangeSeq) Head() AddrRange {
   131  	if ars.length == 0 {
   132  		panic("empty AddrRangeSeq")
   133  	}
   134  	if ars.length == 1 {
   135  		return AddrRange{ars.offset, ars.offset + ars.limit}
   136  	}
   137  	ar := *(*AddrRange)(ars.data)
   138  	ar.Start += ars.offset
   139  	if ar.Length() > ars.limit {
   140  		ar.End = ar.Start + ars.limit
   141  	}
   142  	return ar
   143  }
   144  
   145  // Tail returns an AddrRangeSeq consisting of all AddrRanges in ars after the
   146  // first.
   147  //
   148  // Preconditions: !ars.IsEmpty().
   149  func (ars AddrRangeSeq) Tail() AddrRangeSeq {
   150  	if ars.length == 0 {
   151  		panic("empty AddrRangeSeq")
   152  	}
   153  	if ars.length == 1 {
   154  		return AddrRangeSeq{}
   155  	}
   156  	return ars.externalTail()
   157  }
   158  
   159  // Preconditions: ars.length >= 2.
   160  func (ars AddrRangeSeq) externalTail() AddrRangeSeq {
   161  	data := (*AddrRange)(ars.data)
   162  	headLen := data.Length() - ars.offset
   163  	var tailLimit int64
   164  	if ars.limit > headLen {
   165  		tailLimit = int64(ars.limit - headLen)
   166  	}
   167  	extSlice := gohacks.Slice(data, ars.length)
   168  	return addrRangeSeqFromSliceLimited(extSlice[1:], tailLimit)
   169  }
   170  
   171  // DropFirst returns an AddrRangeSeq equivalent to ars, but with the first n
   172  // bytes omitted. If n > ars.NumBytes(), DropFirst returns an empty
   173  // AddrRangeSeq.
   174  //
   175  // If !ars.IsEmpty() and ars.Head().Length() == 0, DropFirst will always omit
   176  // at least ars.Head(), even if n == 0. This guarantees that the basic pattern
   177  // of:
   178  //
   179  //	for !ars.IsEmpty() {
   180  //	  n, err = doIOWith(ars.Head())
   181  //	  if err != nil {
   182  //	    return err
   183  //	  }
   184  //	  ars = ars.DropFirst(n)
   185  //	}
   186  //
   187  // works even in the presence of zero-length AddrRanges.
   188  //
   189  // Preconditions: n >= 0.
   190  func (ars AddrRangeSeq) DropFirst(n int) AddrRangeSeq {
   191  	if n < 0 {
   192  		panic(fmt.Sprintf("invalid n: %d", n))
   193  	}
   194  	return ars.DropFirst64(int64(n))
   195  }
   196  
   197  // DropFirst64 is equivalent to DropFirst but takes an int64.
   198  func (ars AddrRangeSeq) DropFirst64(n int64) AddrRangeSeq {
   199  	if n < 0 {
   200  		panic(fmt.Sprintf("invalid n: %d", n))
   201  	}
   202  	if Addr(n) > ars.limit {
   203  		return AddrRangeSeq{}
   204  	}
   205  	// Handle initial empty AddrRange.
   206  	switch ars.length {
   207  	case 0:
   208  		return AddrRangeSeq{}
   209  	case 1:
   210  		if ars.limit == 0 {
   211  			return AddrRangeSeq{}
   212  		}
   213  	default:
   214  		if rawHeadLen := (*AddrRange)(ars.data).Length(); ars.offset == rawHeadLen {
   215  			ars = ars.externalTail()
   216  		}
   217  	}
   218  	for n != 0 {
   219  		// Calling ars.Head() here is surprisingly expensive, so inline getting
   220  		// the head's length.
   221  		var headLen Addr
   222  		if ars.length == 1 {
   223  			headLen = ars.limit
   224  		} else {
   225  			headLen = (*AddrRange)(ars.data).Length() - ars.offset
   226  		}
   227  		if Addr(n) < headLen {
   228  			// Dropping ends partway through the head AddrRange.
   229  			ars.offset += Addr(n)
   230  			ars.limit -= Addr(n)
   231  			return ars
   232  		}
   233  		n -= int64(headLen)
   234  		ars = ars.Tail()
   235  	}
   236  	return ars
   237  }
   238  
   239  // TakeFirst returns an AddrRangeSeq equivalent to ars, but iterating at most n
   240  // bytes. TakeFirst never removes AddrRanges from ars; AddrRanges beyond the
   241  // first n bytes are reduced to a length of zero, but will still be iterated.
   242  //
   243  // Preconditions: n >= 0.
   244  func (ars AddrRangeSeq) TakeFirst(n int) AddrRangeSeq {
   245  	if n < 0 {
   246  		panic(fmt.Sprintf("invalid n: %d", n))
   247  	}
   248  	return ars.TakeFirst64(int64(n))
   249  }
   250  
   251  // TakeFirst64 is equivalent to TakeFirst but takes an int64.
   252  func (ars AddrRangeSeq) TakeFirst64(n int64) AddrRangeSeq {
   253  	if n < 0 {
   254  		panic(fmt.Sprintf("invalid n: %d", n))
   255  	}
   256  	if ars.limit > Addr(n) {
   257  		ars.limit = Addr(n)
   258  	}
   259  	return ars
   260  }
   261  
   262  // String implements fmt.Stringer.String.
   263  func (ars AddrRangeSeq) String() string {
   264  	// This is deliberately chosen to be the same as fmt's automatic stringer
   265  	// for []AddrRange.
   266  	var buf bytes.Buffer
   267  	buf.WriteByte('[')
   268  	var sep string
   269  	for !ars.IsEmpty() {
   270  		buf.WriteString(sep)
   271  		sep = " "
   272  		buf.WriteString(ars.Head().String())
   273  		ars = ars.Tail()
   274  	}
   275  	buf.WriteByte(']')
   276  	return buf.String()
   277  }