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

     1  //go:build !sqlite3_nosys
     2  
     3  package util
     4  
     5  import (
     6  	"math"
     7  	"reflect"
     8  	"unsafe"
     9  
    10  	"github.com/tetratelabs/wazero/experimental"
    11  	"golang.org/x/sys/windows"
    12  )
    13  
    14  func virtualAlloc(cap, max uint64) experimental.LinearMemory {
    15  	// Round up to the page size.
    16  	rnd := uint64(windows.Getpagesize() - 1)
    17  	max = (max + rnd) &^ rnd
    18  
    19  	if max > math.MaxInt {
    20  		// This ensures uintptr(max) overflows to a large value,
    21  		// and windows.VirtualAlloc returns an error.
    22  		max = math.MaxUint64
    23  	}
    24  
    25  	// Reserve max bytes of address space, to ensure we won't need to move it.
    26  	// This does not commit memory.
    27  	r, err := windows.VirtualAlloc(0, uintptr(max), windows.MEM_RESERVE, windows.PAGE_READWRITE)
    28  	if err != nil {
    29  		panic(err)
    30  	}
    31  
    32  	mem := virtualMemory{addr: r}
    33  	// SliceHeader, although deprecated, avoids a go vet warning.
    34  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&mem.buf))
    35  	sh.Cap = int(max) // Not a bug.
    36  	sh.Data = r
    37  	return &mem
    38  }
    39  
    40  // The slice covers the entire mmapped memory:
    41  //   - len(buf) is the already committed memory,
    42  //   - cap(buf) is the reserved address space.
    43  type virtualMemory struct {
    44  	buf  []byte
    45  	addr uintptr
    46  }
    47  
    48  func (m *virtualMemory) Reallocate(size uint64) []byte {
    49  	com := uint64(len(m.buf))
    50  	res := uint64(cap(m.buf))
    51  	if com < size && size < res {
    52  		// Round up to the page size.
    53  		rnd := uint64(windows.Getpagesize() - 1)
    54  		new := (size + rnd) &^ rnd
    55  
    56  		// Commit additional memory up to new bytes.
    57  		_, err := windows.VirtualAlloc(m.addr, uintptr(new), windows.MEM_COMMIT, windows.PAGE_READWRITE)
    58  		if err != nil {
    59  			panic(err)
    60  		}
    61  
    62  		// Update committed memory.
    63  		m.buf = m.buf[:new]
    64  	}
    65  	// Limit returned capacity because bytes beyond
    66  	// len(m.buf) have not yet been committed.
    67  	return m.buf[:size:len(m.buf)]
    68  }
    69  
    70  func (m *virtualMemory) Free() {
    71  	err := windows.VirtualFree(m.addr, 0, windows.MEM_RELEASE)
    72  	if err != nil {
    73  		panic(err)
    74  	}
    75  	m.addr = 0
    76  }