// Copyright 2020 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package buffer import ( "gvisor.dev/gvisor/pkg/safemem" ) // WriteBlock returns this buffer as a write Block. func (b *buffer) WriteBlock() safemem.Block { return safemem.BlockFromSafeSlice(b.WriteSlice()) } // ReadBlock returns this buffer as a read Block. func (b *buffer) ReadBlock() safemem.Block { return safemem.BlockFromSafeSlice(b.ReadSlice()) } // WriteFromBlocks implements safemem.Writer.WriteFromBlocks. // // This will advance the write index. func (v *View) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) { need := int(srcs.NumBytes()) if need == 0 { return 0, nil } var ( dst safemem.BlockSeq blocks []safemem.Block ) // Need at least one buffer. firstBuf := v.data.Back() if firstBuf == nil { firstBuf = bufferPool.Get().(*buffer) v.data.PushBack(firstBuf) } // Does the last block have sufficient capacity alone? if l := firstBuf.WriteSize(); l >= need { dst = safemem.BlockSeqOf(firstBuf.WriteBlock()) } else { // Append blocks until sufficient. need -= l blocks = append(blocks, firstBuf.WriteBlock()) for need > 0 { emptyBuf := bufferPool.Get().(*buffer) v.data.PushBack(emptyBuf) need -= emptyBuf.WriteSize() blocks = append(blocks, emptyBuf.WriteBlock()) } dst = safemem.BlockSeqFromSlice(blocks) } // Perform the copy. n, err := safemem.CopySeq(dst, srcs) v.size += int64(n) // Update all indices. for left := int(n); left > 0; firstBuf = firstBuf.Next() { if l := firstBuf.WriteSize(); left >= l { firstBuf.WriteMove(l) // Whole block. left -= l } else { firstBuf.WriteMove(left) // Partial block. left = 0 } } return n, err } // ReadToBlocks implements safemem.Reader.ReadToBlocks. // // This will not advance the read index; the caller should follow // this call with a call to TrimFront in order to remove the read // data from the buffer. This is done to support pipe sematics. func (v *View) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { need := int(dsts.NumBytes()) if need == 0 { return 0, nil } var ( src safemem.BlockSeq blocks []safemem.Block ) firstBuf := v.data.Front() if firstBuf == nil { return 0, nil // No EOF. } // Is all the data in a single block? if l := firstBuf.ReadSize(); l >= need { src = safemem.BlockSeqOf(firstBuf.ReadBlock()) } else { // Build a list of all the buffers. need -= l blocks = append(blocks, firstBuf.ReadBlock()) for buf := firstBuf.Next(); buf != nil && need > 0; buf = buf.Next() { need -= buf.ReadSize() blocks = append(blocks, buf.ReadBlock()) } src = safemem.BlockSeqFromSlice(blocks) } // Perform the copy. n, err := safemem.CopySeq(dsts, src) // See above: we would normally advance the read index here, but we // don't do that in order to support pipe semantics. We rely on a // separate call to TrimFront() in this case. return n, err }