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 }