github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/internal/util/alloc_unix.go (about)

     1  //go:build unix && !sqlite3_nosys
     2  
     3  package util
     4  
     5  import (
     6  	"math"
     7  
     8  	"github.com/tetratelabs/wazero/experimental"
     9  	"golang.org/x/sys/unix"
    10  )
    11  
    12  func virtualAlloc(cap, max uint64) experimental.LinearMemory {
    13  	// Round up to the page size.
    14  	rnd := uint64(unix.Getpagesize() - 1)
    15  	max = (max + rnd) &^ rnd
    16  
    17  	if max > math.MaxInt {
    18  		// This ensures int(max) overflows to a negative value,
    19  		// and unix.Mmap returns EINVAL.
    20  		max = math.MaxUint64
    21  	}
    22  
    23  	// Reserve max bytes of address space, to ensure we won't need to move it.
    24  	// A protected, private, anonymous mapping should not commit memory.
    25  	b, err := unix.Mmap(-1, 0, int(max), unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANON)
    26  	if err != nil {
    27  		panic(err)
    28  	}
    29  	return &mmappedMemory{buf: b[:0]}
    30  }
    31  
    32  // The slice covers the entire mmapped memory:
    33  //   - len(buf) is the already committed memory,
    34  //   - cap(buf) is the reserved address space.
    35  type mmappedMemory struct {
    36  	buf []byte
    37  }
    38  
    39  func (m *mmappedMemory) Reallocate(size uint64) []byte {
    40  	com := uint64(len(m.buf))
    41  	res := uint64(cap(m.buf))
    42  	if com < size && size < res {
    43  		// Round up to the page size.
    44  		rnd := uint64(unix.Getpagesize() - 1)
    45  		new := (size + rnd) &^ rnd
    46  
    47  		// Commit additional memory up to new bytes.
    48  		err := unix.Mprotect(m.buf[com:new], unix.PROT_READ|unix.PROT_WRITE)
    49  		if err != nil {
    50  			panic(err)
    51  		}
    52  
    53  		// Update committed memory.
    54  		m.buf = m.buf[:new]
    55  	}
    56  	// Limit returned capacity because bytes beyond
    57  	// len(m.buf) have not yet been committed.
    58  	return m.buf[:size:len(m.buf)]
    59  }
    60  
    61  func (m *mmappedMemory) Free() {
    62  	err := unix.Munmap(m.buf[:cap(m.buf)])
    63  	if err != nil {
    64  		panic(err)
    65  	}
    66  	m.buf = nil
    67  }