Repository navigation
Expand file tree
/
Copy pathmodule.ae
More file actions
156 lines (138 loc) · 5.33 KB
/
Copy pathmodule.ae
File metadata and controls
156 lines (138 loc) · 5.33 KB
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// std.floatarr - Fixed-size packed-double buffer (float twin of std.intarr)
//
// For SVG path-command args, rasterizer edge tables, bbox accumulators,
// blur kernel coefficients, and other hot paths where std.list's
// void*-boxed items cost an allocation per entry. Size is fixed at
// allocation — callers that need growth use std.list instead.
// Aether's `float` is a 64-bit C double, so the buffer is packed
// doubles end-to-end (not f32).
//
// API shape mirrors std.intarr exactly:
// - Raw externs bounds-check on read (return 0.0 on OOB) and write
// (no-op on OOB). Fast path uses the `_unchecked` variants.
// - Go-style wrappers (`new`, `get`, `set`) return tuples/errors
// for callers that want to distinguish "absent" from "stored zero".
exports(
new, new_filled, get, set,
floatarr_new_raw, floatarr_new_filled_raw,
floatarr_size, floatarr_get_raw, floatarr_set_raw,
floatarr_get_unchecked, floatarr_set_unchecked,
floatarr_data, array,
floatarr_fill, floatarr_free
)
@c_include("aether_arr_inline.h")
// #1986: the header carries the unchecked accessors as `static
// inline`, so an element access is the load it names rather than a
// call the C compiler cannot see through — a matmul over a packed
// buffer was 6.8x the same loop over a raw one before this.
// ---- Raw externs (escape hatch; also the hot-path API) ----
// Allocate a FloatArray of `size` doubles, zero-initialised. Returns
// null if `size` is negative or allocation fails.
extern floatarr_new_raw(size: int) -> ptr
// Allocate and fill every index with `init`.
extern floatarr_new_filled_raw(size: int, init: float) -> ptr
// Number of elements. -1 if `arr` is null.
extern floatarr_size(arr: ptr) -> int
// Read at `i`. Returns 0.0 if `arr` is null or `i` is out-of-range.
// Use `floatarr.get` (below) to distinguish stored-zero from OOB.
extern floatarr_get_raw(arr: ptr, i: int) -> float
// Write `value` at `i`. No-op if `arr` is null or `i` is out-of-range.
// Use `floatarr.set` (below) if you need an error on OOB.
extern floatarr_set_raw(arr: ptr, i: int, value: float)
// Hot-path variants — caller guarantees index is in [0, size). OOB
// access is undefined behaviour. Don't use from outside a tight loop.
extern floatarr_data(arr: ptr) -> float[] @c_import
extern floatarr_get_unchecked(arr: ptr, i: int) -> float @c_import
extern floatarr_set_unchecked(arr: ptr, i: int, value: float) @c_import
// Fill every element with `value`.
extern floatarr_fill(arr: ptr, value: float)
// Release. Idempotent on null.
extern floatarr_free(arr: ptr)
// ---- Go-style wrappers ----
// Allocate a FloatArray of `size` elements, zero-initialised.
// Returns (ptr, "") on success, (null, error) on failure.
new(size: int) -> {
if size < 0 {
return null, "negative size"
}
a = floatarr_new_raw(size)
if a == null {
return null, "allocation failed"
}
return a, ""
}
// Allocate and fill with `init` at every index.
new_filled(size: int, init: float) -> {
if size < 0 {
return null, "negative size"
}
a = floatarr_new_filled_raw(size, init)
if a == null {
return null, "allocation failed"
}
return a, ""
}
// Bounds-checked read. Returns (value, "") on success, (0.0, error)
// for null array or OOB index. Use the raw `floatarr_get_raw`
// directly (imported above) when you don't need to tell absent apart
// from stored-zero — it's one fewer function call and one fewer branch.
get(arr: ptr, i: int) -> {
if arr == null {
return 0.0, "null array"
}
n = floatarr_size(arr)
if i < 0 {
return 0.0, "index out of range"
}
if i >= n {
return 0.0, "index out of range"
}
return floatarr_get_raw(arr, i), ""
}
// Bounds-checked write. Returns "" on success, error for null array
// or OOB index. Use the raw `floatarr_set_raw` directly in hot paths
// where a no-op-on-OOB is acceptable.
set(arr: ptr, i: int, value: float) -> {
if arr == null {
return "null array"
}
n = floatarr_size(arr)
if i < 0 {
return "index out of range"
}
if i >= n {
return "index out of range"
}
floatarr_set_raw(arr, i, value)
return ""
}
// array(arr) -- the buffer as a plain `float[]`, so `v[i]` works (#2041).
//
// `a[i]` on the HANDLE cannot work: the handle is a bare `ptr`, and `[]`
// has no element type to dispatch on. A view is typed, so it does:
//
// v = floatarr.array(a)
// v[3] = 1.5
// total = total + v[3]
//
// and the compiler lowers `v[i]` to the same load `floatarr_get_unchecked`
// inlines to, so the readable spelling costs nothing. `std.strarr` has had
// this shape since it was written.
//
// The view is bounded by the array's size (#2301): `v.len` is `floatarr.size(a)`
// and `v[i]` outside it is the slice bounds panic, not a read past the
// buffer. Pass it to std.sort (`sort.floats(v)`), or any `float[]` parameter,
// whole or as a sub-slice. A null handle is the empty slice.
//
// BORROWED, not owned: valid until the handle is freed. For the unchecked
// load in a hot loop, `floatarr_get_unchecked` is still there.
floatarr_array(arr: ptr) -> float[] {
if arr == null {
return null
}
return floatarr_data(arr)[0..floatarr_size(arr)]
}
// The short name, beside new / get / set: `floatarr.array(a)`.
array(arr: ptr) -> float[] {
return floatarr_array(arr)
}