nomen-lang 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/System/Array.nm +20 -0
- package/core/System/Buffer.nm +78 -2
- package/core/System/ClassBuffer.nm +45 -0
- package/core/System/LinkedList.nm +16 -0
- package/core/System/List.nm +21 -1
- package/core/System/Map.nm +45 -11
- package/core/System/Set.nm +41 -9
- package/core/System/String.nm +18 -0
- package/core/docs/System/Array.md +2 -0
- package/dist/index.mjs +1751 -84
- package/package.json +1 -1
package/core/System/Array.nm
CHANGED
|
@@ -160,6 +160,26 @@ pub struct Array<T>: Viewable {
|
|
|
160
160
|
```
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// Bounds-checked accessors — the runtime-checked escape hatch for
|
|
164
|
+
// indices the constraint checker can't prove (see PERF.md gap 5). Unlike
|
|
165
|
+
// every other Array method these have Nomen-level bodies: the method-call
|
|
166
|
+
// receiver conventions for Array receivers (first-element pointer on
|
|
167
|
+
// aarch64, struct Array_<T>* on C) are passed through unchanged, and the
|
|
168
|
+
// bodies read self.length / self.at through them.
|
|
169
|
+
pub func at_or = (self, int index, T fallback, out T) {
|
|
170
|
+
if index >= 0 && index < self.length {
|
|
171
|
+
return self.at(index)
|
|
172
|
+
}
|
|
173
|
+
return fallback
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
pub func at_or_panic = (self, int index, out T) {
|
|
177
|
+
if index >= 0 && index < self.length {
|
|
178
|
+
return self.at(index)
|
|
179
|
+
}
|
|
180
|
+
panic("index out of range")
|
|
181
|
+
}
|
|
182
|
+
|
|
163
183
|
pub func at_end = (self, out T) {
|
|
164
184
|
```
|
|
165
185
|
#arch: c
|
package/core/System/Buffer.nm
CHANGED
|
@@ -356,7 +356,11 @@ pub struct Buffer<T> {
|
|
|
356
356
|
```
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
-
|
|
359
|
+
// `inline` splices the (width-folded at monomorphization) raw body at
|
|
360
|
+
// call sites — the load/store pair behind every container `.at`/`set` is
|
|
361
|
+
// the hottest path in data-heavy code, and the naked-inline path keeps it
|
|
362
|
+
// to a handful of instructions with no call overhead.
|
|
363
|
+
inline func load_T = (self, int i: i >= 0 && i < self.cap, out T) {
|
|
360
364
|
```
|
|
361
365
|
#arch: c
|
|
362
366
|
return ((T*)(unsigned long long)self->data)[i];
|
|
@@ -399,7 +403,7 @@ pub struct Buffer<T> {
|
|
|
399
403
|
```
|
|
400
404
|
}
|
|
401
405
|
|
|
402
|
-
func store_T = (ref self, int i: i >= 0 && i < self.cap, T val) {
|
|
406
|
+
inline func store_T = (ref self, int i: i >= 0 && i < self.cap, T val) {
|
|
403
407
|
```
|
|
404
408
|
#arch: c
|
|
405
409
|
((T*)(unsigned long long)self->data)[i] = val;
|
|
@@ -549,6 +553,78 @@ pub struct Buffer<T> {
|
|
|
549
553
|
```
|
|
550
554
|
}
|
|
551
555
|
|
|
556
|
+
// Move slot src into slot dst: overwrite dst with src's bytes and zero
|
|
557
|
+
// src, so exactly ONE slot owns the value afterwards. The
|
|
558
|
+
// ownership-transferring primitive behind backward-shift deletion
|
|
559
|
+
// (Map/Set.remove) — unlike store_T, which gives the slot its own deep
|
|
560
|
+
// copy and leaves the source slot intact, a shift transfers the source's
|
|
561
|
+
// heap pointers wholesale. For owning elements (string / value structs
|
|
562
|
+
// with string fields) the build replaces this body with a specialized
|
|
563
|
+
// version that also reclaims dst's previous value first (see
|
|
564
|
+
// owning_buffer_specialize.ts); this raw form is correct for
|
|
565
|
+
// trivially-destructible elements only.
|
|
566
|
+
inline func shift_T = (ref self, int dst: dst >= 0 && dst < self.cap, int src: src >= 0 && src < self.cap) {
|
|
567
|
+
```
|
|
568
|
+
#arch: c
|
|
569
|
+
T* _slots = (T*)(unsigned long long)self->data;
|
|
570
|
+
if (dst != src) {
|
|
571
|
+
_slots[dst] = _slots[src];
|
|
572
|
+
memset((void*)&_slots[src], 0, T_SIZE);
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
```
|
|
576
|
+
#arch: aarch64
|
|
577
|
+
// x19 = self, x1 = dst, x2 = src
|
|
578
|
+
ldr x4, [x19, #8]
|
|
579
|
+
mov x3, #T_SIZE
|
|
580
|
+
madd x5, x1, x3, x4
|
|
581
|
+
madd x6, x2, x3, x4
|
|
582
|
+
cmp x5, x6
|
|
583
|
+
b.eq .Lbuffer_shift_T_end
|
|
584
|
+
cmp x3, #8
|
|
585
|
+
b.gt .Lbuffer_shift_T_copy
|
|
586
|
+
// Width-matched move: an 8-byte access on a sub-8 element would
|
|
587
|
+
// clobber the following slot (and run past the slab's end).
|
|
588
|
+
cmp x3, #1
|
|
589
|
+
b.eq .Lbuffer_shift_T_1
|
|
590
|
+
cmp x3, #2
|
|
591
|
+
b.eq .Lbuffer_shift_T_2
|
|
592
|
+
cmp x3, #4
|
|
593
|
+
b.eq .Lbuffer_shift_T_4
|
|
594
|
+
ldr x7, [x6]
|
|
595
|
+
str x7, [x5]
|
|
596
|
+
str xzr, [x6]
|
|
597
|
+
b .Lbuffer_shift_T_end
|
|
598
|
+
.Lbuffer_shift_T_1:
|
|
599
|
+
ldrb w7, [x6]
|
|
600
|
+
strb w7, [x5]
|
|
601
|
+
strb wzr, [x6]
|
|
602
|
+
b .Lbuffer_shift_T_end
|
|
603
|
+
.Lbuffer_shift_T_2:
|
|
604
|
+
ldrh w7, [x6]
|
|
605
|
+
strh w7, [x5]
|
|
606
|
+
strh wzr, [x6]
|
|
607
|
+
b .Lbuffer_shift_T_end
|
|
608
|
+
.Lbuffer_shift_T_4:
|
|
609
|
+
ldr w7, [x6]
|
|
610
|
+
str w7, [x5]
|
|
611
|
+
str wzr, [x6]
|
|
612
|
+
b .Lbuffer_shift_T_end
|
|
613
|
+
.Lbuffer_shift_T_copy:
|
|
614
|
+
mov x0, x5
|
|
615
|
+
mov x1, x6
|
|
616
|
+
mov x2, x3
|
|
617
|
+
stp x5, x6, [sp, #-16]!
|
|
618
|
+
bl _memcpy
|
|
619
|
+
ldp x5, x6, [sp], #16
|
|
620
|
+
mov x0, x6
|
|
621
|
+
mov x1, #0
|
|
622
|
+
mov x2, x3
|
|
623
|
+
bl _memset
|
|
624
|
+
.Lbuffer_shift_T_end:
|
|
625
|
+
```
|
|
626
|
+
}
|
|
627
|
+
|
|
552
628
|
// A non-owning (ptr, len) slice [start, end) over this buffer's element
|
|
553
629
|
// storage, returned as a `view T`. It borrows from self: it may not outlive
|
|
554
630
|
// self's scope and is invalidated if self is reassigned. This is the single
|
|
@@ -299,6 +299,51 @@ pub struct ClassBuffer<T> {
|
|
|
299
299
|
```
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
+
// Move slot src into slot dst: reclaim dst's previous instance (destroy +
|
|
303
|
+
// free), take over src's pointer, and zero src — exactly one slot owns the
|
|
304
|
+
// instance afterwards. The class-buffer counterpart of Buffer.shift_T,
|
|
305
|
+
// used by Map/Set.remove's backward-shift so shifting entries neither
|
|
306
|
+
// duplicates nor orphans owned instances.
|
|
307
|
+
func shift_T = (ref self, int dst: dst >= 0 && dst < self.cap, int src: src >= 0 && src < self.cap) {
|
|
308
|
+
```
|
|
309
|
+
#arch: c
|
|
310
|
+
long *slots = (long*)(unsigned long long)self->data;
|
|
311
|
+
if (dst != src) {
|
|
312
|
+
if (slots[dst]) {
|
|
313
|
+
T_destroy((void*)slots[dst]);
|
|
314
|
+
free((void*)slots[dst]);
|
|
315
|
+
}
|
|
316
|
+
slots[dst] = slots[src];
|
|
317
|
+
slots[src] = 0;
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
```
|
|
321
|
+
#arch: aarch64
|
|
322
|
+
stp x20, x21, [sp, #-16]!
|
|
323
|
+
str x22, [sp, #-16]!
|
|
324
|
+
ldr x9, [x19, #8]
|
|
325
|
+
lsl x3, x1, #3
|
|
326
|
+
add x20, x9, x3
|
|
327
|
+
lsl x3, x2, #3
|
|
328
|
+
add x21, x9, x3
|
|
329
|
+
cmp x20, x21
|
|
330
|
+
b.eq .Lcb_shift_T_done
|
|
331
|
+
ldr x0, [x20]
|
|
332
|
+
cbz x0, .Lcb_shift_T_move
|
|
333
|
+
mov x22, x0
|
|
334
|
+
bl T_destroy
|
|
335
|
+
mov x0, x22
|
|
336
|
+
bl _free
|
|
337
|
+
.Lcb_shift_T_move:
|
|
338
|
+
ldr x9, [x21]
|
|
339
|
+
str x9, [x20]
|
|
340
|
+
str xzr, [x21]
|
|
341
|
+
.Lcb_shift_T_done:
|
|
342
|
+
ldr x22, [sp], #16
|
|
343
|
+
ldp x20, x21, [sp], #16
|
|
344
|
+
```
|
|
345
|
+
}
|
|
346
|
+
|
|
302
347
|
// A non-owning (ptr, len) slice [start, end) over this buffer's class
|
|
303
348
|
// pointers, returned as a `view T` (T is a class, so each element is an
|
|
304
349
|
// 8-byte pointer). Borrows from self — invalidated on reassignment. Mirrors
|
|
@@ -57,6 +57,22 @@ pub struct LinkedList<T>: Enumerable {
|
|
|
57
57
|
return self.values.load_T(idx)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// Bounds-checked access: `fallback` when idx is outside [0, self.count).
|
|
61
|
+
pub func at_or = (self, int idx, T fallback, out T) {
|
|
62
|
+
if idx >= 0 && idx < self.count {
|
|
63
|
+
return self.at(idx)
|
|
64
|
+
}
|
|
65
|
+
return fallback
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Bounds-checked access that traps when idx is outside [0, self.count).
|
|
69
|
+
pub func at_or_panic = (self, int idx, out T) {
|
|
70
|
+
if idx >= 0 && idx < self.count {
|
|
71
|
+
return self.at(idx)
|
|
72
|
+
}
|
|
73
|
+
panic("index out of range")
|
|
74
|
+
}
|
|
75
|
+
|
|
60
76
|
pub func length = (self, out int) {
|
|
61
77
|
return self.count
|
|
62
78
|
}
|
package/core/System/List.nm
CHANGED
|
@@ -27,10 +27,30 @@ pub struct List<T>: Viewable {
|
|
|
27
27
|
return self.items.move_T(idx)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
pub func at = (self, int i: i >= 0 && i < self.length, out T) {
|
|
30
|
+
pub inline func at = (self, int i: i >= 0 && i < self.length, out T) {
|
|
31
31
|
return self.items.load_T(i)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Bounds-checked access: `fallback` when i is outside [0, self.length) —
|
|
35
|
+
// for indices that can't be proven at compile time and out-of-range is an
|
|
36
|
+
// expected case.
|
|
37
|
+
pub func at_or = (self, int i, T fallback, out T) {
|
|
38
|
+
if i >= 0 && i < self.length {
|
|
39
|
+
return self.at(i)
|
|
40
|
+
}
|
|
41
|
+
return fallback
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Bounds-checked access that traps when i is outside [0, self.length) —
|
|
45
|
+
// for indices that can't be proven at compile time and out-of-range is a
|
|
46
|
+
// bug, not a case.
|
|
47
|
+
pub func at_or_panic = (self, int i, out T) {
|
|
48
|
+
if i >= 0 && i < self.length {
|
|
49
|
+
return self.at(i)
|
|
50
|
+
}
|
|
51
|
+
panic("index out of range")
|
|
52
|
+
}
|
|
53
|
+
|
|
34
54
|
// A non-owning `view T` over [start, end). Delegates to the backing Buffer's
|
|
35
55
|
// #arch slice primitive — no #arch code here. Borrows from self.
|
|
36
56
|
pub func slice = (self, int start: start >= 0, int end: end >= start, out view T) {
|
package/core/System/Map.nm
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* An ordered key-value map with `set`/`get`/`has`/`remove`. Keys must be
|
|
2
|
+
* An ordered key-value map with `set`/`get`/`get_or`/`has`/`remove`. Keys must be
|
|
3
3
|
* `Hashable` (hashed to a `uint` via `key.hash()`) and `Equatable` (compared
|
|
4
4
|
* with `==`); values may be any type.
|
|
5
|
+
*
|
|
6
|
+
* The capacity is always a power of two (initial 8, doubling on rehash), so
|
|
7
|
+
* bucket indexing is a mask (`hash & (cap - 1)`) instead of a division — and
|
|
8
|
+
* it is correct for negative hashes too: two's-complement `&` already lands
|
|
9
|
+
* in `[0, cap)`, which is why no negative adjustment is needed.
|
|
5
10
|
**/
|
|
6
11
|
pub struct Map<TK: Hashable + Equatable, TV> {
|
|
7
12
|
var length = 0
|
|
@@ -53,6 +58,34 @@ pub struct Map<TK: Hashable + Equatable, TV> {
|
|
|
53
58
|
return self.values.load_T(idx)
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
// Fused lookup-with-fallback: one probe instead of the `has` + `get`
|
|
62
|
+
// pair (which probes twice) for the common "default when missing" shape.
|
|
63
|
+
pub func get_or = (self, TK key, TV fallback, out TV) {
|
|
64
|
+
var int cap = self.keys.cap
|
|
65
|
+
if cap == 0 {
|
|
66
|
+
return fallback
|
|
67
|
+
}
|
|
68
|
+
var int idx = self.find_slot(key, cap)
|
|
69
|
+
if self.used.load(idx) == 0 {
|
|
70
|
+
return fallback
|
|
71
|
+
}
|
|
72
|
+
return self.values.load_T(idx)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Lookup that traps when the key is absent — for when a missing key is
|
|
76
|
+
// a bug, not a case (and `get`'s 0-for-missing would be ambiguous).
|
|
77
|
+
pub func get_or_panic = (self, TK key, out TV) {
|
|
78
|
+
var int cap = self.keys.cap
|
|
79
|
+
if cap == 0 {
|
|
80
|
+
panic("key not found")
|
|
81
|
+
}
|
|
82
|
+
var int idx = self.find_slot(key, cap)
|
|
83
|
+
if self.used.load(idx) == 0 {
|
|
84
|
+
panic("key not found")
|
|
85
|
+
}
|
|
86
|
+
return self.values.load_T(idx)
|
|
87
|
+
}
|
|
88
|
+
|
|
56
89
|
pub func has = (self, TK key, out bool) {
|
|
57
90
|
var int cap = self.keys.cap
|
|
58
91
|
if cap == 0 {
|
|
@@ -91,10 +124,7 @@ pub struct Map<TK: Hashable + Equatable, TV> {
|
|
|
91
124
|
if self.used.load(k) == 0 {
|
|
92
125
|
break
|
|
93
126
|
}
|
|
94
|
-
var int home = (self.keys.load_T(k).hash() as int)
|
|
95
|
-
if home < 0 {
|
|
96
|
-
home = home + cap
|
|
97
|
-
}
|
|
127
|
+
var int home = (self.keys.load_T(k).hash() as int) & (cap - 1)
|
|
98
128
|
var int dgap = gap - home
|
|
99
129
|
if dgap < 0 {
|
|
100
130
|
dgap = dgap + cap
|
|
@@ -104,8 +134,13 @@ pub struct Map<TK: Hashable + Equatable, TV> {
|
|
|
104
134
|
dk = dk + cap
|
|
105
135
|
}
|
|
106
136
|
if dgap < dk {
|
|
107
|
-
|
|
108
|
-
|
|
137
|
+
// shift_T (not store_T): transfers the entry's owned pointers
|
|
138
|
+
// wholesale and reclaims the displaced slot's value. store_T
|
|
139
|
+
// would strdup a fresh copy into gap while leaving the source
|
|
140
|
+
// slot's pointer stranded at the vacated slot — leaked once
|
|
141
|
+
// used.store marks it empty (the buffer destroy skips it).
|
|
142
|
+
self.keys.shift_T(gap, k)
|
|
143
|
+
self.values.shift_T(gap, k)
|
|
109
144
|
self.used.store(gap, 1)
|
|
110
145
|
gap = k
|
|
111
146
|
}
|
|
@@ -115,10 +150,9 @@ pub struct Map<TK: Hashable + Equatable, TV> {
|
|
|
115
150
|
}
|
|
116
151
|
|
|
117
152
|
func find_slot = (self, TK key, int cap, out int: out >= 0 && out < cap) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
153
|
+
// cap is a power of two, so this is `hash mod cap` via a mask —
|
|
154
|
+
// non-negative for any hash (two's complement), no adjustment.
|
|
155
|
+
var int idx = (key.hash() as int) & (cap - 1)
|
|
122
156
|
while self.used.load(idx) != 0 {
|
|
123
157
|
if self.keys.load_T(idx) == key {
|
|
124
158
|
return idx
|
package/core/System/Set.nm
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* An ordered set of unique values (`add`/`has`/`get`/`remove`). Elements must
|
|
3
3
|
* be `Hashable` (hashed to a `uint` via `value.hash()`) and `Equatable`
|
|
4
4
|
* (compared with `==`).
|
|
5
|
+
*
|
|
6
|
+
* The capacity is always a power of two (initial 8, doubling on rehash), so
|
|
7
|
+
* slot indexing is a mask (`hash & (cap - 1)`) — correct for negative hashes
|
|
8
|
+
* too (two's-complement `&` already lands in `[0, cap)`).
|
|
5
9
|
**/
|
|
6
10
|
pub struct Set<T: Hashable + Equatable> {
|
|
7
11
|
var length = 0
|
|
@@ -47,6 +51,33 @@ pub struct Set<T: Hashable + Equatable> {
|
|
|
47
51
|
return self.slots.load_T(idx)
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
// Membership lookup with a fallback value: `fallback` when the value is
|
|
55
|
+
// not in the set (one probe instead of the `has` + `get` pair).
|
|
56
|
+
pub func get_or = (self, T key, T fallback, out T) {
|
|
57
|
+
var int cap = self.slots.cap
|
|
58
|
+
if cap == 0 {
|
|
59
|
+
return fallback
|
|
60
|
+
}
|
|
61
|
+
var int idx = self.find_slot(key, cap)
|
|
62
|
+
if self.used.load(idx) == 0 {
|
|
63
|
+
return fallback
|
|
64
|
+
}
|
|
65
|
+
return self.slots.load_T(idx)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Membership lookup that traps when the value is not in the set.
|
|
69
|
+
pub func get_or_panic = (self, T key, out T) {
|
|
70
|
+
var int cap = self.slots.cap
|
|
71
|
+
if cap == 0 {
|
|
72
|
+
panic("value not found in set")
|
|
73
|
+
}
|
|
74
|
+
var int idx = self.find_slot(key, cap)
|
|
75
|
+
if self.used.load(idx) == 0 {
|
|
76
|
+
panic("value not found in set")
|
|
77
|
+
}
|
|
78
|
+
return self.slots.load_T(idx)
|
|
79
|
+
}
|
|
80
|
+
|
|
50
81
|
pub func remove = (ref self, T value) {
|
|
51
82
|
var int cap = self.slots.cap
|
|
52
83
|
if cap == 0 {
|
|
@@ -76,10 +107,7 @@ pub struct Set<T: Hashable + Equatable> {
|
|
|
76
107
|
if self.used.load(k) == 0 {
|
|
77
108
|
break
|
|
78
109
|
}
|
|
79
|
-
var int home = (self.slots.load_T(k).hash() as int)
|
|
80
|
-
if home < 0 {
|
|
81
|
-
home = home + cap
|
|
82
|
-
}
|
|
110
|
+
var int home = (self.slots.load_T(k).hash() as int) & (cap - 1)
|
|
83
111
|
var int dgap = gap - home
|
|
84
112
|
if dgap < 0 {
|
|
85
113
|
dgap = dgap + cap
|
|
@@ -89,7 +117,12 @@ pub struct Set<T: Hashable + Equatable> {
|
|
|
89
117
|
dk = dk + cap
|
|
90
118
|
}
|
|
91
119
|
if dgap < dk {
|
|
92
|
-
|
|
120
|
+
// shift_T (not store_T): transfers the entry wholesale and
|
|
121
|
+
// reclaims the displaced slot's value — store_T would strdup a
|
|
122
|
+
// fresh copy into gap while stranding the source slot's
|
|
123
|
+
// pointer at the vacated slot (leaked once used.store marks it
|
|
124
|
+
// empty; the buffer destroy skips unused slots).
|
|
125
|
+
self.slots.shift_T(gap, k)
|
|
93
126
|
self.used.store(gap, 1)
|
|
94
127
|
gap = k
|
|
95
128
|
}
|
|
@@ -99,10 +132,9 @@ pub struct Set<T: Hashable + Equatable> {
|
|
|
99
132
|
}
|
|
100
133
|
|
|
101
134
|
func find_slot = (self, T value, int cap, out int: out >= 0 && out < cap) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
135
|
+
// cap is a power of two: `hash mod cap` via a mask, non-negative
|
|
136
|
+
// for any hash — no adjustment needed.
|
|
137
|
+
var int idx = (value.hash() as int) & (cap - 1)
|
|
106
138
|
while self.used.load(idx) != 0 {
|
|
107
139
|
if self.slots.load_T(idx) == value {
|
|
108
140
|
return idx
|
package/core/System/String.nm
CHANGED
|
@@ -51,6 +51,24 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
51
51
|
```
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// Bounds-checked access: `fallback` when index is outside
|
|
55
|
+
// [0, self.length).
|
|
56
|
+
pub func at_or = (self, int index, char fallback, out char) {
|
|
57
|
+
if index >= 0 && index < self.length {
|
|
58
|
+
return self.at(index)
|
|
59
|
+
}
|
|
60
|
+
return fallback
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Bounds-checked access that traps when index is outside
|
|
64
|
+
// [0, self.length) — out-of-range is a bug, not a case.
|
|
65
|
+
pub func at_or_panic = (self, int index, out char) {
|
|
66
|
+
if index >= 0 && index < self.length {
|
|
67
|
+
return self.at(index)
|
|
68
|
+
}
|
|
69
|
+
panic("index out of range")
|
|
70
|
+
}
|
|
71
|
+
|
|
54
72
|
// A non-owning slice [start, end) into self's buffer. Returns a `view
|
|
55
73
|
// string` (ptr, len) that borrows from self: it may not outlive self's
|
|
56
74
|
// scope and is invalidated if self is reassigned. Use .to_string() to
|
|
@@ -7,6 +7,8 @@ A fixed-length array of values, created with a literal (`[1, 2, 3]`) and address
|
|
|
7
7
|
**Members:**
|
|
8
8
|
|
|
9
9
|
- `at(int index) -> T`
|
|
10
|
+
- `at_or(int index, T fallback) -> T`
|
|
11
|
+
- `at_or_panic(int index) -> T`
|
|
10
12
|
- `first() -> T`
|
|
11
13
|
- `set(int index, T value)`
|
|
12
14
|
- `at_end() -> T`
|