Skip to content

Commit 9789948

Browse files
committed
feat(alloc): add memory allocation
1 parent 33f1fbc commit 9789948

File tree

5 files changed

+192
-0
lines changed

5 files changed

+192
-0
lines changed

internal/alloc/alloc_other.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//go:build !unix && !windows
2+
3+
package alloc // import "github.com/ncruces/go-sqlite3/internal/alloc"
4+
5+
import "github.com/tetratelabs/wazero/experimental"
6+
7+
func NewMemory(cap, max uint64) experimental.LinearMemory {
8+
return &sliceMemory{make([]byte, 0, cap)}
9+
}
10+
11+
type sliceMemory struct {
12+
buf []byte
13+
}
14+
15+
func (b *sliceMemory) Free() {}
16+
17+
func (b *sliceMemory) Reallocate(size uint64) []byte {
18+
if cap := uint64(cap(b.buf)); size > cap {
19+
b.buf = append(b.buf[:cap], make([]byte, size-cap)...)
20+
} else {
21+
b.buf = b.buf[:size]
22+
}
23+
return b.buf
24+
}

internal/alloc/alloc_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package alloc_test // import "github.com/ncruces/go-sqlite3/internal/alloc"
2+
3+
import (
4+
"math"
5+
"testing"
6+
7+
"github.com/ncruces/go-sqlite3/internal/alloc"
8+
)
9+
10+
func TestVirtual(t *testing.T) {
11+
defer func() { _ = recover() }()
12+
alloc.NewMemory(math.MaxInt+2, math.MaxInt+2)
13+
t.Error("want panic")
14+
}

internal/alloc/alloc_unix.go

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

internal/alloc/alloc_windows.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package alloc // import "github.com/ncruces/go-sqlite3/internal/alloc"
2+
3+
import (
4+
"math"
5+
"unsafe"
6+
7+
"github.com/tetratelabs/wazero/experimental"
8+
"golang.org/x/sys/windows"
9+
)
10+
11+
func NewMemory(cap, max uint64) experimental.LinearMemory {
12+
// Round up to the page size.
13+
rnd := uint64(windows.Getpagesize() - 1)
14+
res := (max + rnd) &^ rnd
15+
16+
if res > math.MaxInt {
17+
// This ensures uintptr(res) overflows to a large value,
18+
// and windows.VirtualAlloc returns an error.
19+
res = math.MaxUint64
20+
}
21+
22+
com := res
23+
kind := windows.MEM_COMMIT
24+
if cap < max { // Commit memory only if cap=max.
25+
com = 0
26+
kind = windows.MEM_RESERVE
27+
}
28+
29+
// Reserve res bytes of address space, to ensure we won't need to move it.
30+
r, err := windows.VirtualAlloc(0, uintptr(res), uint32(kind), windows.PAGE_READWRITE)
31+
if err != nil {
32+
panic(err)
33+
}
34+
buf := unsafe.Slice((*byte)(unsafe.Pointer(r)), int(max))
35+
mem := virtualMemory{addr: r, buf: buf[:com:res]}
36+
return &mem
37+
}
38+
39+
// The slice covers the entire mmapped memory:
40+
// - len(buf) is the already committed memory,
41+
// - cap(buf) is the reserved address space.
42+
type virtualMemory struct {
43+
buf []byte
44+
addr uintptr
45+
}
46+
47+
func (m *virtualMemory) Reallocate(size uint64) []byte {
48+
com := uint64(len(m.buf))
49+
res := uint64(cap(m.buf))
50+
if com < size && size <= res {
51+
// Grow geometrically, round up to the page size.
52+
rnd := uint64(windows.Getpagesize() - 1)
53+
new := com + com>>3
54+
new = min(max(size, new), res)
55+
new = (new + rnd) &^ rnd
56+
57+
// Commit additional memory up to new bytes.
58+
_, err := windows.VirtualAlloc(m.addr, uintptr(new), windows.MEM_COMMIT, windows.PAGE_READWRITE)
59+
if err != nil {
60+
return nil
61+
}
62+
63+
m.buf = m.buf[:new] // Update committed memory.
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+
}

internal/plugin/driver.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"runtime"
1111
"sync/atomic"
1212

13+
"github.com/OpenListTeam/OpenList/v4/internal/alloc"
1314
"github.com/OpenListTeam/OpenList/v4/internal/driver"
1415
"github.com/OpenListTeam/OpenList/v4/internal/errs"
1516
"github.com/OpenListTeam/OpenList/v4/internal/model"
@@ -28,6 +29,7 @@ import (
2829

2930
"github.com/tetratelabs/wazero"
3031
"github.com/tetratelabs/wazero/api"
32+
"github.com/tetratelabs/wazero/experimental"
3133
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
3234
)
3335

@@ -77,6 +79,7 @@ func NewDriverPlugin(ctx context.Context, plugin *PluginInfo) (*DriverPlugin, er
7779
}).
7880
WithName(plugin.ID)
7981

82+
ctx = experimental.WithMemoryAllocator(ctx, experimental.MemoryAllocatorFunc(alloc.NewMemory))
8083
// 实例化模块,同时注入 Host API
8184
instance, err := rt.InstantiateModule(ctx, compiledModule, moduleConfig)
8285
if err != nil {

0 commit comments

Comments
 (0)