blob: 21d370cee25d6ce402e9dd52745311a62898a4f9 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package iobuf
import "v.io/x/lib/vlog"
// Allocator is an allocator for Slices that tries to allocate contiguously.
// That is, sequential allocations will tend to be contiguous, which means
// that Coalesce() will usually be able to perform coalescing (without
// copying the data).
//
// calloc := iobuf.Allocator(...)
// slice1 := calloc.Alloc(10)
// slice2 := calloc.Alloc(20)
// slices := iobuf.Coalesce([]*iobuf.Slice{slice1, slice2})
// // slices should contain 1 element with length 30.
type Allocator struct {
pool *Pool
iobuf *buf
index uint
reserve uint
}
// NewAllocator returns a new Slice allocator.
//
// <reserve> is the number of spare bytes to reserve at the beginning of
// each allocated Slice. This can be used to reserve space for a header,
// for example.
//
// NOTE: It's a bit weird to set the number of reserve bytes in the NewAllocator
// call; it seems more natural in the Alloc call. But it's convenient to set it
// in NewAllocator, because in our use-cases, the code that calls Alloc doesn't
// know the number of reserve bytes.
func NewAllocator(pool *Pool, reserve uint) *Allocator {
return &Allocator{pool: pool, reserve: reserve}
}
// Release releases the allocator.
func (a *Allocator) Release() {
if a.iobuf != nil {
a.iobuf.release()
a.iobuf = nil
}
a.pool = nil
}
// Alloc allocates a new Slice.
func (a *Allocator) Alloc(bytes uint) *Slice {
n := bytes + a.reserve
if a.iobuf == nil {
if a.pool == nil {
vlog.Info("iobuf.Allocator has already been closed")
return nil
}
a.iobuf = a.pool.alloc(n)
} else if uint(len(a.iobuf.Contents)) < a.index+n {
a.iobuf.release()
a.iobuf = a.pool.alloc(n)
a.index = 0
}
free := a.index
base := free + a.reserve
a.index += uint(n)
return a.iobuf.slice(free, base, a.index)
}
func (a *Allocator) CopyExtra(buf []byte, extra int) *Slice {
slice := a.Alloc(uint(len(buf) + extra))
copy(slice.Contents, buf)
slice.Contents = slice.Contents[:len(buf)]
slice.extra = uint(extra)
return slice
}
// Copy allocates a Slice and copies the buf into it.
func (a *Allocator) Copy(buf []byte) *Slice {
slice := a.Alloc(uint(len(buf)))
copy(slice.Contents, buf)
return slice
}