inet.af/netstack@v0.0.0-20220214151720-7585b01ddccf/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 "inet.af/netstack/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 }