github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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/SagerNet/gvisor/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  	headLen := (*AddrRange)(ars.data).Length() - ars.offset
   162  	var tailLimit int64
   163  	if ars.limit > headLen {
   164  		tailLimit = int64(ars.limit - headLen)
   165  	}
   166  	var extSlice []AddrRange
   167  	extSliceHdr := (*gohacks.SliceHeader)(unsafe.Pointer(&extSlice))
   168  	extSliceHdr.Data = ars.data
   169  	extSliceHdr.Len = ars.length
   170  	extSliceHdr.Cap = ars.length
   171  	return addrRangeSeqFromSliceLimited(extSlice[1:], tailLimit)
   172  }
   173  
   174  // DropFirst returns an AddrRangeSeq equivalent to ars, but with the first n
   175  // bytes omitted. If n > ars.NumBytes(), DropFirst returns an empty
   176  // AddrRangeSeq.
   177  //
   178  // If !ars.IsEmpty() and ars.Head().Length() == 0, DropFirst will always omit
   179  // at least ars.Head(), even if n == 0. This guarantees that the basic pattern
   180  // of:
   181  //
   182  //     for !ars.IsEmpty() {
   183  //       n, err = doIOWith(ars.Head())
   184  //       if err != nil {
   185  //         return err
   186  //       }
   187  //       ars = ars.DropFirst(n)
   188  //     }
   189  //
   190  // works even in the presence of zero-length AddrRanges.
   191  //
   192  // Preconditions: n >= 0.
   193  func (ars AddrRangeSeq) DropFirst(n int) AddrRangeSeq {
   194  	if n < 0 {
   195  		panic(fmt.Sprintf("invalid n: %d", n))
   196  	}
   197  	return ars.DropFirst64(int64(n))
   198  }
   199  
   200  // DropFirst64 is equivalent to DropFirst but takes an int64.
   201  func (ars AddrRangeSeq) DropFirst64(n int64) AddrRangeSeq {
   202  	if n < 0 {
   203  		panic(fmt.Sprintf("invalid n: %d", n))
   204  	}
   205  	if Addr(n) > ars.limit {
   206  		return AddrRangeSeq{}
   207  	}
   208  	// Handle initial empty AddrRange.
   209  	switch ars.length {
   210  	case 0:
   211  		return AddrRangeSeq{}
   212  	case 1:
   213  		if ars.limit == 0 {
   214  			return AddrRangeSeq{}
   215  		}
   216  	default:
   217  		if rawHeadLen := (*AddrRange)(ars.data).Length(); ars.offset == rawHeadLen {
   218  			ars = ars.externalTail()
   219  		}
   220  	}
   221  	for n != 0 {
   222  		// Calling ars.Head() here is surprisingly expensive, so inline getting
   223  		// the head's length.
   224  		var headLen Addr
   225  		if ars.length == 1 {
   226  			headLen = ars.limit
   227  		} else {
   228  			headLen = (*AddrRange)(ars.data).Length() - ars.offset
   229  		}
   230  		if Addr(n) < headLen {
   231  			// Dropping ends partway through the head AddrRange.
   232  			ars.offset += Addr(n)
   233  			ars.limit -= Addr(n)
   234  			return ars
   235  		}
   236  		n -= int64(headLen)
   237  		ars = ars.Tail()
   238  	}
   239  	return ars
   240  }
   241  
   242  // TakeFirst returns an AddrRangeSeq equivalent to ars, but iterating at most n
   243  // bytes. TakeFirst never removes AddrRanges from ars; AddrRanges beyond the
   244  // first n bytes are reduced to a length of zero, but will still be iterated.
   245  //
   246  // Preconditions: n >= 0.
   247  func (ars AddrRangeSeq) TakeFirst(n int) AddrRangeSeq {
   248  	if n < 0 {
   249  		panic(fmt.Sprintf("invalid n: %d", n))
   250  	}
   251  	return ars.TakeFirst64(int64(n))
   252  }
   253  
   254  // TakeFirst64 is equivalent to TakeFirst but takes an int64.
   255  func (ars AddrRangeSeq) TakeFirst64(n int64) AddrRangeSeq {
   256  	if n < 0 {
   257  		panic(fmt.Sprintf("invalid n: %d", n))
   258  	}
   259  	if ars.limit > Addr(n) {
   260  		ars.limit = Addr(n)
   261  	}
   262  	return ars
   263  }
   264  
   265  // String implements fmt.Stringer.String.
   266  func (ars AddrRangeSeq) String() string {
   267  	// This is deliberately chosen to be the same as fmt's automatic stringer
   268  	// for []AddrRange.
   269  	var buf bytes.Buffer
   270  	buf.WriteByte('[')
   271  	var sep string
   272  	for !ars.IsEmpty() {
   273  		buf.WriteString(sep)
   274  		sep = " "
   275  		buf.WriteString(ars.Head().String())
   276  		ars = ars.Tail()
   277  	}
   278  	buf.WriteByte(']')
   279  	return buf.String()
   280  }