github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/safemem/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 safemem 16 17 import ( 18 "bytes" 19 "fmt" 20 "unsafe" 21 22 "golang.org/x/sys/unix" 23 "github.com/SagerNet/gvisor/pkg/gohacks" 24 ) 25 26 // A BlockSeq represents a sequence of Blocks, each of which has non-zero 27 // length. 28 // 29 // BlockSeqs are immutable and may be copied by value. The zero value of 30 // BlockSeq represents an empty sequence. 31 type BlockSeq struct { 32 // If length is 0, then the BlockSeq is empty. Invariants: data == 0; 33 // offset == 0; limit == 0. 34 // 35 // If length is -1, then the BlockSeq represents the single Block{data, 36 // limit, false}. Invariants: offset == 0; limit > 0; limit does not 37 // overflow the range of an int. 38 // 39 // If length is -2, then the BlockSeq represents the single Block{data, 40 // limit, true}. Invariants: offset == 0; limit > 0; limit does not 41 // overflow the range of an int. 42 // 43 // Otherwise, length >= 2, and the BlockSeq represents the `length` Blocks 44 // in the array of Blocks starting at address `data`, starting at `offset` 45 // bytes into the first Block and limited to the following `limit` bytes. 46 // Invariants: data != 0; offset < len(data[0]); limit > 0; offset+limit <= 47 // the combined length of all Blocks in the array; the first Block in the 48 // array has non-zero length. 49 // 50 // length is never 1; sequences consisting of a single Block are always 51 // stored inline (with length < 0). 52 data unsafe.Pointer 53 length int 54 offset int 55 limit uint64 56 } 57 58 // BlockSeqOf returns a BlockSeq representing the single Block b. 59 func BlockSeqOf(b Block) BlockSeq { 60 if b.length == 0 { 61 return BlockSeq{} 62 } 63 bs := BlockSeq{ 64 data: b.start, 65 length: -1, 66 limit: uint64(b.length), 67 } 68 if b.needSafecopy { 69 bs.length = -2 70 } 71 return bs 72 } 73 74 // BlockSeqFromSlice returns a BlockSeq representing all Blocks in slice. 75 // If slice contains Blocks with zero length, BlockSeq will skip them during 76 // iteration. 77 // 78 // Whether the returned BlockSeq shares memory with slice is unspecified; 79 // clients should avoid mutating slices passed to BlockSeqFromSlice. 80 // 81 // Preconditions: The combined length of all Blocks in slice <= math.MaxUint64. 82 func BlockSeqFromSlice(slice []Block) BlockSeq { 83 slice = skipEmpty(slice) 84 var limit uint64 85 for _, b := range slice { 86 sum := limit + uint64(b.Len()) 87 if sum < limit { 88 panic("BlockSeq length overflows uint64") 89 } 90 limit = sum 91 } 92 return blockSeqFromSliceLimited(slice, limit) 93 } 94 95 // Preconditions: 96 // * The combined length of all Blocks in slice <= limit. 97 // * If len(slice) != 0, the first Block in slice has non-zero length and 98 // limit > 0. 99 func blockSeqFromSliceLimited(slice []Block, limit uint64) BlockSeq { 100 switch len(slice) { 101 case 0: 102 return BlockSeq{} 103 case 1: 104 return BlockSeqOf(slice[0].TakeFirst64(limit)) 105 default: 106 return BlockSeq{ 107 data: unsafe.Pointer(&slice[0]), 108 length: len(slice), 109 limit: limit, 110 } 111 } 112 } 113 114 func skipEmpty(slice []Block) []Block { 115 for i, b := range slice { 116 if b.Len() != 0 { 117 return slice[i:] 118 } 119 } 120 return nil 121 } 122 123 // IsEmpty returns true if bs contains no Blocks. 124 // 125 // Invariants: bs.IsEmpty() == (bs.NumBlocks() == 0) == (bs.NumBytes() == 0). 126 // (Of these, prefer to use bs.IsEmpty().) 127 func (bs BlockSeq) IsEmpty() bool { 128 return bs.length == 0 129 } 130 131 // NumBlocks returns the number of Blocks in bs. 132 func (bs BlockSeq) NumBlocks() int { 133 // In general, we have to count: if bs represents a windowed slice then the 134 // slice may contain Blocks with zero length, and bs.length may be larger 135 // than the actual number of Blocks due to bs.limit. 136 var n int 137 for !bs.IsEmpty() { 138 n++ 139 bs = bs.Tail() 140 } 141 return n 142 } 143 144 // NumBytes returns the sum of Block.Len() for all Blocks in bs. 145 func (bs BlockSeq) NumBytes() uint64 { 146 return bs.limit 147 } 148 149 // Head returns the first Block in bs. 150 // 151 // Preconditions: !bs.IsEmpty(). 152 func (bs BlockSeq) Head() Block { 153 if bs.length == 0 { 154 panic("empty BlockSeq") 155 } 156 if bs.length < 0 { 157 return bs.internalBlock() 158 } 159 return (*Block)(bs.data).DropFirst(bs.offset).TakeFirst64(bs.limit) 160 } 161 162 // Preconditions: bs.length < 0. 163 func (bs BlockSeq) internalBlock() Block { 164 return Block{ 165 start: bs.data, 166 length: int(bs.limit), 167 needSafecopy: bs.length == -2, 168 } 169 } 170 171 // Tail returns a BlockSeq consisting of all Blocks in bs after the first. 172 // 173 // Preconditions: !bs.IsEmpty(). 174 func (bs BlockSeq) Tail() BlockSeq { 175 if bs.length == 0 { 176 panic("empty BlockSeq") 177 } 178 if bs.length < 0 { 179 return BlockSeq{} 180 } 181 head := (*Block)(bs.data).DropFirst(bs.offset) 182 headLen := uint64(head.Len()) 183 if headLen >= bs.limit { 184 // The head Block exhausts the limit, so the tail is empty. 185 return BlockSeq{} 186 } 187 var extSlice []Block 188 extSliceHdr := (*gohacks.SliceHeader)(unsafe.Pointer(&extSlice)) 189 extSliceHdr.Data = bs.data 190 extSliceHdr.Len = bs.length 191 extSliceHdr.Cap = bs.length 192 tailSlice := skipEmpty(extSlice[1:]) 193 tailLimit := bs.limit - headLen 194 return blockSeqFromSliceLimited(tailSlice, tailLimit) 195 } 196 197 // DropFirst returns a BlockSeq equivalent to bs, but with the first n bytes 198 // omitted. If n > bs.NumBytes(), DropFirst returns an empty BlockSeq. 199 // 200 // Preconditions: n >= 0. 201 func (bs BlockSeq) DropFirst(n int) BlockSeq { 202 if n < 0 { 203 panic(fmt.Sprintf("invalid n: %d", n)) 204 } 205 return bs.DropFirst64(uint64(n)) 206 } 207 208 // DropFirst64 is equivalent to DropFirst but takes an uint64. 209 func (bs BlockSeq) DropFirst64(n uint64) BlockSeq { 210 if n >= bs.limit { 211 return BlockSeq{} 212 } 213 for { 214 // Calling bs.Head() here is surprisingly expensive, so inline getting 215 // the head's length. 216 var headLen uint64 217 if bs.length < 0 { 218 headLen = bs.limit 219 } else { 220 headLen = uint64((*Block)(bs.data).Len() - bs.offset) 221 } 222 if n < headLen { 223 // Dropping ends partway through the head Block. 224 if bs.length < 0 { 225 return BlockSeqOf(bs.internalBlock().DropFirst64(n)) 226 } 227 bs.offset += int(n) 228 bs.limit -= n 229 return bs 230 } 231 n -= headLen 232 bs = bs.Tail() 233 } 234 } 235 236 // TakeFirst returns a BlockSeq equivalent to the first n bytes of bs. If n > 237 // bs.NumBytes(), TakeFirst returns a BlockSeq equivalent to bs. 238 // 239 // Preconditions: n >= 0. 240 func (bs BlockSeq) TakeFirst(n int) BlockSeq { 241 if n < 0 { 242 panic(fmt.Sprintf("invalid n: %d", n)) 243 } 244 return bs.TakeFirst64(uint64(n)) 245 } 246 247 // TakeFirst64 is equivalent to TakeFirst but takes a uint64. 248 func (bs BlockSeq) TakeFirst64(n uint64) BlockSeq { 249 if n == 0 { 250 return BlockSeq{} 251 } 252 if bs.limit > n { 253 bs.limit = n 254 } 255 return bs 256 } 257 258 // String implements fmt.Stringer.String. 259 func (bs BlockSeq) String() string { 260 var buf bytes.Buffer 261 buf.WriteByte('[') 262 var sep string 263 for !bs.IsEmpty() { 264 buf.WriteString(sep) 265 sep = " " 266 buf.WriteString(bs.Head().String()) 267 bs = bs.Tail() 268 } 269 buf.WriteByte(']') 270 return buf.String() 271 } 272 273 // CopySeq copies srcs.NumBytes() or dsts.NumBytes() bytes, whichever is less, 274 // from srcs to dsts and returns the number of bytes copied. 275 // 276 // If srcs and dsts overlap, the data stored in dsts is unspecified. 277 func CopySeq(dsts, srcs BlockSeq) (uint64, error) { 278 var done uint64 279 for !dsts.IsEmpty() && !srcs.IsEmpty() { 280 dst := dsts.Head() 281 src := srcs.Head() 282 n, err := Copy(dst, src) 283 done += uint64(n) 284 if err != nil { 285 return done, err 286 } 287 dsts = dsts.DropFirst(n) 288 srcs = srcs.DropFirst(n) 289 } 290 return done, nil 291 } 292 293 // ZeroSeq sets all bytes in dsts to 0 and returns the number of bytes zeroed. 294 func ZeroSeq(dsts BlockSeq) (uint64, error) { 295 var done uint64 296 for !dsts.IsEmpty() { 297 n, err := Zero(dsts.Head()) 298 done += uint64(n) 299 if err != nil { 300 return done, err 301 } 302 dsts = dsts.DropFirst(n) 303 } 304 return done, nil 305 } 306 307 // IovecsFromBlockSeq returns a []unix.Iovec representing seq. 308 func IovecsFromBlockSeq(bs BlockSeq) []unix.Iovec { 309 iovs := make([]unix.Iovec, 0, bs.NumBlocks()) 310 for ; !bs.IsEmpty(); bs = bs.Tail() { 311 b := bs.Head() 312 iovs = append(iovs, unix.Iovec{ 313 Base: &b.ToSlice()[0], 314 Len: uint64(b.Len()), 315 }) 316 // We don't need to care about b.NeedSafecopy(), because the host 317 // kernel will handle such address ranges just fine (by returning 318 // EFAULT). 319 } 320 return iovs 321 }