-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathmodule.ae
More file actions
262 lines (233 loc) · 11.6 KB
/
Copy pathmodule.ae
File metadata and controls
262 lines (233 loc) · 11.6 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// std.bytes — mutable byte buffer with random-access write and
// overlap-safe forward copy_within.
//
// Aether's `string` is immutable. For workloads that need to write
// bytes at arbitrary indices (binary codec output buffers, varint
// emit, frame layout, output accumulation), reach for std.bytes.
// Lifecycle: bytes.new() → set / copy_from_string / copy_within →
// bytes.finish(b, len) to hand off to a refcounted AetherString,
// or bytes.free(b) to discard. Issue #288.
//
// Why not just use repeated string.concat?
// Concat is allocate-and-copy on every step (O(n²) for n appends),
// and there's no way to "set byte at index 2 to whatever's at index 0
// after writing index 2 myself" — concat reads from a snapshot.
// std.bytes is the answer for any algorithm whose write at offset i
// depends on a write the caller just made at offset i - run_length
// (RLE expansion is the canonical case).
import std.string
exports(
aether_bytes_new, aether_bytes_set, aether_bytes_get,
aether_bytes_length,
aether_bytes_set_le16, aether_bytes_get_le16,
aether_bytes_set_le32, aether_bytes_get_le32,
aether_bytes_set_le64, aether_bytes_get_le64,
aether_bytes_set_be16, aether_bytes_get_be16,
aether_bytes_set_be32, aether_bytes_get_be32,
aether_bytes_set_be64, aether_bytes_get_be64,
aether_bytes_copy_from_string, aether_bytes_copy_from_bytes,
aether_bytes_copy_within,
aether_bytes_finish, aether_bytes_to_string, aether_bytes_free,
aether_bytes_data, aether_bytes_capacity, aether_bytes_set_length,
new, set, get, length,
set_le16, get_le16, set_le32, get_le32, set_le64, get_le64,
set_be16, get_be16, set_be32, get_be32, set_be64, get_be64,
copy_from_string, copy_from_bytes, copy_within, finish, to_string, free,
data, capacity, set_length
)
// ---- Raw externs ----
extern aether_bytes_new(initial_capacity: int) -> ptr
extern aether_bytes_length(b: ptr) -> int
extern aether_bytes_capacity(b: ptr) -> int
extern aether_bytes_set_length(b: ptr, length: int) -> int
extern aether_bytes_data(b: ptr) -> ptr
extern aether_bytes_set(b: ptr, index: int, value: int) -> int
extern aether_bytes_get(b: ptr, index: int) -> int
extern aether_bytes_set_le16(b: ptr, index: int, value: int) -> int
extern aether_bytes_get_le16(b: ptr, index: int) -> int
extern aether_bytes_set_le32(b: ptr, index: int, value: int) -> int
extern aether_bytes_get_le32(b: ptr, index: int) -> int
extern aether_bytes_set_le64(b: ptr, index: int, value: long) -> int
extern aether_bytes_get_le64(b: ptr, index: int) -> long
extern aether_bytes_set_be16(b: ptr, index: int, value: int) -> int
extern aether_bytes_get_be16(b: ptr, index: int) -> int
extern aether_bytes_set_be32(b: ptr, index: int, value: int) -> int
extern aether_bytes_get_be32(b: ptr, index: int) -> int
extern aether_bytes_set_be64(b: ptr, index: int, value: long) -> int
extern aether_bytes_get_be64(b: ptr, index: int) -> long
extern aether_bytes_copy_from_string(b: ptr, dst: int, src: string, src_len: int) -> int
// Two-buffer copy. `dst` and `src` are distinct AetherBytes pointers;
// `length` is the count of bytes to copy from `src` at offset
// `src_off` into `dst` at offset `dst_off`. Grows `dst` if needed.
// For an in-place copy (same buffer) use `copy_within` — it has the
// deliberate RLE forward-overlap semantics that memmove() lacks.
extern aether_bytes_copy_from_bytes(dst: ptr, dst_off: int, src: ptr, src_off: int, length: int) -> int
// `length` here is the count of bytes to copy, not a buffer length.
extern aether_bytes_copy_within(b: ptr, dst: int, src: int, length: int) -> int
// `aether_bytes_finish` returns a freshly heap-owned AetherString
// (`string_new_with_length` — see std/bytes/aether_bytes.c). The
// `string @heap` return tells the heap-string tracker the caller
// owns the result, so `x = bytes.finish(...)` gets a `_heap_x = 1`
// slot and any function returning a `bytes.finish` value is itself
// classified heap-returning. Mirrors `aether_strbuilder_finish`.
extern aether_bytes_finish(b: ptr, length: int) -> string @heap
// Non-consuming twin of finish: mint a fresh heap AetherString from the
// first `length` bytes without destroying the buffer, so the buffer's
// owner can keep it and mint again. See to_string wrapper below.
extern aether_bytes_to_string(b: ptr, length: int) -> string @heap
extern aether_bytes_free(b: ptr)
// ---- Aether-side wrappers ----
// Allocate a new mutable byte buffer with at least `initial_capacity`
// bytes reserved. Returns null on allocation failure or negative
// capacity. Initial logical length is 0.
// Borrowed pointer to the buffer's own memory, for handing the region to a
// foreign runtime without copying it byte by byte. Valid until the next call
// that can grow the buffer, which may move the allocation and invalidate it.
data(b: ptr) -> ptr { return aether_bytes_data(b) }
// Bytes reserved, which is at least length and may be more.
capacity(b: ptr) -> int { return aether_bytes_capacity(b) }
// Publish how many bytes a direct write into data()'s region made live.
// Clamped to the capacity; returns the length actually set.
set_length(b: ptr, length: int) -> int { return aether_bytes_set_length(b, length) }
new(initial_capacity: int) -> ptr {
return aether_bytes_new(initial_capacity)
}
// Number of bytes the buffer logically contains. Returns -1 if `b`
// is null.
length(b: ptr) -> int {
return aether_bytes_length(b)
}
// Write a single byte at `index`. The buffer grows if needed; gaps
// between the previous tail and `index` are zero-filled. Returns
// 1 on success, 0 on failure. The parameter is named `value` (not
// `byte`) because `byte` became a reserved keyword for the new
// primitive type. The third arg's runtime semantics are unchanged
// — it stores the low 8 bits at `index`.
set(b: ptr, index: int, value: int) -> int {
return aether_bytes_set(b, index, value)
}
// Read the byte previously written at `index`, returned as 0..255.
// Returns -1 if `b` is null, `index` is negative, or `index` is past
// the buffer's logical length. Pairs with `set` so a caller can build
// then walk a buffer in-place — the canonical binary-codec encoder
// pattern (svndiff and similar).
get(b: ptr, index: int) -> int {
return aether_bytes_get(b, index)
}
// Little-endian 16-bit write at `index..index+1`. Grows the buffer
// if needed. Returns 1 on success, 0 on failure (NULL buffer or
// negative index).
set_le16(b: ptr, index: int, value: int) -> int {
return aether_bytes_set_le16(b, index, value)
}
// Little-endian 16-bit read at `index..index+1`. Returns the
// unsigned 16-bit value as an int, or -1 on failure (NULL buffer,
// negative index, or range past current length).
get_le16(b: ptr, index: int) -> int {
return aether_bytes_get_le16(b, index)
}
// Little-endian 32-bit write at `index..index+3`. Grows the buffer
// if needed. Returns 1 on success, 0 on failure.
set_le32(b: ptr, index: int, value: int) -> int {
return aether_bytes_set_le32(b, index, value)
}
// Little-endian 32-bit read at `index..index+3`. Returns the value
// as an int (host-endian after reassembly), or -1 on failure.
// Round-trips losslessly with set_le32 for any int input.
get_le32(b: ptr, index: int) -> int {
return aether_bytes_get_le32(b, index)
}
// Little-endian 64-bit write at `index..index+7` (least-significant
// byte first). Grows the buffer if needed. Returns 1 on success, 0 on
// failure.
set_le64(b: ptr, index: int, value: long) -> int {
return aether_bytes_set_le64(b, index, value)
}
// Little-endian 64-bit read at `index..index+7`. Returns the value as
// a long, or -1 on failure. Round-trips losslessly with set_le64 for
// any long input.
get_le64(b: ptr, index: int) -> long {
return aether_bytes_get_le64(b, index)
}
// Big-endian 16-bit write at `index..index+1` (most-significant byte
// first). Grows the buffer if needed. Returns 1 on success, 0 on
// failure (NULL buffer or negative index).
set_be16(b: ptr, index: int, value: int) -> int {
return aether_bytes_set_be16(b, index, value)
}
// Big-endian 16-bit read at `index..index+1`. Returns the unsigned
// 16-bit value as an int, or -1 on failure (NULL buffer, negative
// index, or range past current length).
get_be16(b: ptr, index: int) -> int {
return aether_bytes_get_be16(b, index)
}
// Big-endian 32-bit write at `index..index+3` (most-significant byte
// first). Grows the buffer if needed. Returns 1 on success, 0 on
// failure.
set_be32(b: ptr, index: int, value: int) -> int {
return aether_bytes_set_be32(b, index, value)
}
// Big-endian 32-bit read at `index..index+3`. Returns the value as an
// int (top bit reads back as the sign bit), or -1 on failure.
// Round-trips losslessly with set_be32 for any int input.
get_be32(b: ptr, index: int) -> int {
return aether_bytes_get_be32(b, index)
}
// Big-endian 64-bit write at `index..index+7` (most-significant byte
// first). Grows the buffer if needed. Returns 1 on success, 0 on
// failure.
set_be64(b: ptr, index: int, value: long) -> int {
return aether_bytes_set_be64(b, index, value)
}
// Big-endian 64-bit read at `index..index+7`. Returns the value as a
// long, or -1 on failure. Round-trips losslessly with set_be64.
get_be64(b: ptr, index: int) -> long {
return aether_bytes_get_be64(b, index)
}
// Copy `src_len` bytes from `src` into the buffer starting at offset
// `dst`. `src` may be either a plain `string` literal or an
// AetherString-bearing value (binary-safe via aether_string_data on
// the C side). Returns 1 on success, 0 on failure.
copy_from_string(b: ptr, dst: int, src: string, src_len: int) -> int {
return aether_bytes_copy_from_string(b, dst, src, src_len)
}
// Copy `length` bytes from `src` buffer at offset `src_off` into
// `dst` buffer at offset `dst_off`. The buffers must be distinct;
// for an in-place copy use `copy_within`. Grows the destination
// buffer if needed. Returns 1 on success, 0 on failure (NULL buffer,
// negative offsets/length, source range past src's logical length,
// or destination grow OOM). Canonical use: two-pass separable
// Gaussian blur (pixels → temp → pixels).
copy_from_bytes(dst: ptr, dst_off: int, src: ptr, src_off: int, length: int) -> int {
return aether_bytes_copy_from_bytes(dst, dst_off, src, src_off, length)
}
// Copy `length` bytes from offset `src` to offset `dst` *within the
// same buffer*, forward byte-by-byte. Bytes written earlier in the
// same call are visible to later reads — the RLE-overlap behaviour.
// Returns 1 on success, 0 on failure (NULL buffer, negative offsets,
// or src + length > current buffer length).
copy_within(b: ptr, dst: int, src: int, length: int) -> int {
return aether_bytes_copy_within(b, dst, src, length)
}
// Hand the buffer off to a refcounted AetherString and destroy the
// AetherBytes wrapper. After this call, `b` is invalid. The string
// carries the explicit length so embedded NULs survive end-to-end.
// Pass the prefix you actually want to keep (use bytes.length(b) to
// take the whole buffer).
finish(b: ptr, length: int) -> string {
return aether_bytes_finish(b, length)
}
// Non-consuming counterpart to finish: return a fresh refcounted string
// copy of the buffer's first `length` bytes WITHOUT destroying the
// buffer. Use when you want to keep owning the AetherBytes (e.g. a struct
// field that holds the payload) and read it out as a string on demand.
// The returned string is independent of `b`; embedded NULs survive.
to_string(b: ptr, length: int) -> string {
return aether_bytes_to_string(b, length)
}
// Discard the buffer without converting to a string. Idempotent on
// null. Use this for error paths that mid-way decide the buffer
// isn't needed.
free(b: ptr) {
aether_bytes_free(b)
}