summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-ffmpreg/wasm/funcs.go
blob: a0a199ca14c862af61bef4b7748c74b8cffd035d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package wasm

import (
	"context"

	"github.com/tetratelabs/wazero/api"
	"github.com/tetratelabs/wazero/experimental"
)

type snapshotskey struct{}

type snapshotctx struct {
	context.Context
	snaps *snapshots
}

func (ctx snapshotctx) Value(key any) any {
	if _, ok := key.(snapshotskey); ok {
		return ctx.snaps
	}
	return ctx.Context.Value(key)
}

const ringsz uint = 8

type snapshots struct {
	r [ringsz]struct {
		eptr uint32
		snap experimental.Snapshot
	}
	n uint
}

func (s *snapshots) get(envptr uint32) experimental.Snapshot {
	start := (s.n % ringsz)

	for i := start; i != ^uint(0); i-- {
		if s.r[i].eptr == envptr {
			snap := s.r[i].snap
			s.r[i].eptr = 0
			s.r[i].snap = nil
			s.n = i - 1
			return snap
		}
	}

	for i := ringsz - 1; i > start; i-- {
		if s.r[i].eptr == envptr {
			snap := s.r[i].snap
			s.r[i].eptr = 0
			s.r[i].snap = nil
			s.n = i - 1
			return snap
		}
	}

	panic("snapshot not found")
}

func (s *snapshots) set(envptr uint32, snapshot experimental.Snapshot) {
	start := (s.n % ringsz)

	for i := start; i < ringsz; i++ {
		switch s.r[i].eptr {
		case 0, envptr:
			s.r[i].eptr = envptr
			s.r[i].snap = snapshot
			s.n = i
			return
		}
	}

	for i := uint(0); i < start; i++ {
		switch s.r[i].eptr {
		case 0, envptr:
			s.r[i].eptr = envptr
			s.r[i].snap = snapshot
			s.n = i
			return
		}
	}

	panic("snapshots full")
}

// withSetjmpLongjmp updates the context to contain wazero/experimental.Snapshotter{} support,
// and embeds the necessary snapshots map required for later calls to Setjmp() / Longjmp().
func withSetjmpLongjmp(ctx context.Context) context.Context {
	return snapshotctx{Context: experimental.WithSnapshotter(ctx), snaps: new(snapshots)}
}

func getSnapshots(ctx context.Context) *snapshots {
	v, _ := ctx.Value(snapshotskey{}).(*snapshots)
	return v
}

// setjmp implements the C function: setjmp(env jmp_buf)
func setjmp(ctx context.Context, _ api.Module, stack []uint64) {

	// Input arguments.
	envptr := api.DecodeU32(stack[0])

	// Take snapshot of current execution environment.
	snapshotter := experimental.GetSnapshotter(ctx)
	snapshot := snapshotter.Snapshot()

	// Get stored snapshots map.
	snapshots := getSnapshots(ctx)

	// Set latest snapshot in map.
	snapshots.set(envptr, snapshot)

	// Set return.
	stack[0] = 0
}

// longjmp implements the C function: int longjmp(env jmp_buf, value int)
func longjmp(ctx context.Context, _ api.Module, stack []uint64) {

	// Input arguments.
	envptr := api.DecodeU32(stack[0])
	// val := stack[1]

	// Get stored snapshots map.
	snapshots := getSnapshots(ctx)
	if snapshots == nil {
		panic("setjmp / longjmp not supported")
	}

	// Get snapshot stored in map.
	snapshot := snapshots.get(envptr)

	// Set return.
	stack[0] = 0

	// Restore execution and
	// return passed value arg.
	snapshot.Restore(stack[1:])
}