caixa_ast/span.rs
1//! Byte-offset spans — minimal and cheap. Line/column are computed on demand.
2
3use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7/// A half-open byte range `[start, end)` into some source string.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
9pub struct Span {
10 pub start: u32,
11 pub end: u32,
12}
13
14impl Span {
15 #[must_use]
16 pub const fn new(start: u32, end: u32) -> Self {
17 Self { start, end }
18 }
19
20 #[must_use]
21 pub const fn point(offset: u32) -> Self {
22 Self {
23 start: offset,
24 end: offset,
25 }
26 }
27
28 /// Byte-width of the half-open range `[start, end)`. `pub const fn` —
29 /// `u32::saturating_sub` is const-stable since Rust 1.47, well before
30 /// this workspace's 1.89 MSRV floor, so the promotion is a body-
31 /// preserving type-signature widening. Matches the sibling
32 /// [`Self::new`] / [`Self::point`] / [`Self::contains`] /
33 /// [`Self::union`] `pub const fn` shape on the same [`Span`] primitive
34 /// — every downstream consumer that wants a compile-time span-width
35 /// fixture (a `const WIDTH: u32 = SPAN.len();` LSP hover-registry
36 /// entry, a per-diagnostic const-context width oracle a future
37 /// admission webhook consults, a compile-time span-partition truth
38 /// table the caixa-fmt trivia-owner resolver keys off) now reads
39 /// through one substrate-primitive const dispatch rather than being
40 /// forced onto the runtime code path.
41 #[must_use]
42 pub const fn len(self) -> u32 {
43 self.end.saturating_sub(self.start)
44 }
45
46 /// Half-open emptiness predicate — `true` iff `self.start == self.end`.
47 /// `pub const fn` — folds onto the sibling [`Self::len`] `pub const
48 /// fn` promotion (integer equality is const in Rust since long before
49 /// this workspace's 1.89 MSRV floor). Matches every other accessor /
50 /// predicate on this [`Span`] primitive's const-eval surface; only
51 /// the fundamentally-runtime-only `slice(&str)` method (string-slice
52 /// indexing outside const-eval) remains `pub fn`.
53 #[must_use]
54 pub const fn is_empty(self) -> bool {
55 self.len() == 0
56 }
57
58 /// The smallest span covering both. Useful for building a list node's
59 /// span from its children — every parser code path that composes a
60 /// parent span from its immediate child boundaries reads through this
61 /// (`open.union(close)` on a delimited list, `head.union(target.span)`
62 /// on a quote-form target, and every downstream trivia-owner /
63 /// diagnostic-aggregator / fmt-region parent-span builder). `pub const
64 /// fn` — the body reaches for `u32::min` / `u32::max`, both const
65 /// stable since Rust 1.83 (well before this workspace's 1.89 MSRV
66 /// floor), so the promotion is a body-preserving type-signature
67 /// widening. Matches the sibling [`Self::new`] / [`Self::point`] /
68 /// [`Self::contains`] `pub const fn` shape on the same [`Span`]
69 /// primitive — every downstream consumer that wants a compile-time
70 /// span-composition fixture (a `const OUTER: Span = INNER1.union(
71 /// INNER2);` LSP hover-registry entry, a per-diagnostic const-context
72 /// parent-span oracle a future admission webhook consults, a
73 /// compile-time span-partition truth table the caixa-fmt trivia-owner
74 /// resolver keys off) now reads through one substrate-primitive const
75 /// dispatch rather than being forced onto the runtime code path.
76 #[must_use]
77 pub const fn union(self, other: Span) -> Span {
78 // Body-preserving `Ord::min` / `Ord::max` open-coding: the trait
79 // dispatch is not yet stable in `const` context (rust-lang/rust
80 // #143874), so the pub-const-fn promotion reads through inline
81 // `if`/`else` on the same `u32 < u32` / `u32 > u32` comparisons
82 // the primitive-integer inherent methods lower to.
83 let start = if self.start < other.start {
84 self.start
85 } else {
86 other.start
87 };
88 let end = if self.end > other.end {
89 self.end
90 } else {
91 other.end
92 };
93 Span { start, end }
94 }
95
96 #[must_use]
97 pub fn slice<'a>(self, src: &'a str) -> &'a str {
98 let start = self.start as usize;
99 let end = self.end as usize;
100 if start >= src.len() {
101 ""
102 } else {
103 let end = end.min(src.len());
104 &src[start..end]
105 }
106 }
107
108 /// Byte-offset half-open containment predicate every consumer that
109 /// keys off an author-authored source position (LSP hover
110 /// span-lookup at the cursor, per-diagnostic span-registry probe,
111 /// per-trivia leading/trailing-owner attachment gate) reads through
112 /// — returns `true` iff `offset` lies inside the half-open range
113 /// `[self.start, self.end)`. `pub const fn` — matches the sibling
114 /// [`Self::new`] / [`Self::point`] `pub const fn` shape on the same
115 /// [`Span`] primitive's construction axis, extending the const-eval
116 /// surface onto the primitive's containment-predicate axis without
117 /// a body change (integer comparison is const in Rust since long
118 /// before this workspace's 1.89 MSRV floor). Every downstream
119 /// consumer that wants a compile-time span-containment fixture —
120 /// a `const IS_INSIDE: bool = SPAN.contains(OFFSET);` LSP hover-
121 /// registry entry, a per-diagnostic const-context span oracle a
122 /// future admission webhook consults, a compile-time span-partition
123 /// truth table the caixa-fmt trivia-owner resolver keys off — now
124 /// reads through one substrate-primitive const dispatch rather than
125 /// being forced onto the runtime code path.
126 #[must_use]
127 pub const fn contains(self, offset: u32) -> bool {
128 offset >= self.start && offset < self.end
129 }
130}
131
132impl fmt::Display for Span {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(f, "{}..{}", self.start, self.end)
135 }
136}
137
138/// 1-indexed line/column pair — what humans see in editors.
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140pub struct Position {
141 pub line: u32,
142 pub column: u32,
143}
144
145impl Position {
146 /// Substrate-primitive constructor every producer of a
147 /// 1-indexed line/column pair reads through — folds the two-slot
148 /// `Position { line, column }` struct-literal wire-up (the sole
149 /// production emitter [`line_column`]'s tail, plus every
150 /// per-fixture line/column literal in the sibling test module)
151 /// onto one `pub const fn` dispatch. Matches the sibling
152 /// [`Span::new`] / [`Span::point`] `pub const fn` shape on the
153 /// same caixa-ast source-position primitive family; every
154 /// downstream LSP hover-registry / diagnostic emitter / future
155 /// position-carrying trivia-registry that wants a compile-time
156 /// position fixture (a `const AT: Position = Position::new(1, 4);`
157 /// LSP hover-oracle default, a per-diagnostic const-context
158 /// leading-position fixture, a compile-time position-partition
159 /// truth table the caixa-fmt trivia-owner resolver keys off)
160 /// now reaches through one substrate-primitive const dispatch
161 /// rather than duplicating the two-slot struct literal at every
162 /// construction site.
163 #[must_use]
164 pub const fn new(line: u32, column: u32) -> Self {
165 Self { line, column }
166 }
167
168 /// Canonical origin — line 1, column 1, matching the
169 /// [`line_column`] convention (both axes 1-indexed). `pub const
170 /// fn` — the shape materialises at compile time so a future
171 /// const-context consumer (a substrate-wide `const ORIGIN:
172 /// Position = Position::origin();` LSP hover-oracle default, a
173 /// per-diagnostic const-context leading-position fixture that
174 /// today reaches for the `(1, 1)` magic pair inline) reads
175 /// through one substrate-primitive const dispatch rather than
176 /// duplicating the `(1, 1)` origin literal at every consumer.
177 #[must_use]
178 pub const fn origin() -> Self {
179 Self::new(1, 1)
180 }
181}
182
183impl fmt::Display for Position {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 write!(f, "{}:{}", self.line, self.column)
186 }
187}
188
189/// Compute (line, column) for a byte offset. Line and column are 1-indexed.
190/// O(offset); fine for diagnostics, not for hot paths.
191///
192/// `pub const fn` — closes the const-eval discipline on the caixa-ast
193/// source-position primitive family. The pre-lift body iterated
194/// [`str::char_indices`], whose iterator methods are not yet const-
195/// stable (rust-lang/rust #143874, the same tracking issue [`Span::union`]
196/// open-codes `Ord::min` / `Ord::max` around). The const-lift trades the
197/// char-boundary iterator for a manual `while` walk over the src's raw
198/// [`str::as_bytes`] (`pub const fn` since Rust 1.32, well before this
199/// workspace's 1.89 MSRV floor) that keys off two facts every `&str`
200/// carries by construction:
201///
202/// - **`\n` is ASCII.** Byte `0x0A` is a 1-byte UTF-8 codepoint and
203/// never appears as a continuation byte of a multi-byte codepoint (a
204/// continuation byte lies in `0x80..0xC0`). Every line boundary in a
205/// `&str` therefore surfaces as a lone `b'\n'` at exactly one byte
206/// position, so the line-count decision matches the pre-lift
207/// `char_indices` walk verbatim.
208/// - **A char boundary is a non-continuation byte.** Every codepoint
209/// start byte lies in `0x00..0x80` (ASCII) or `0xC0..` (multi-byte
210/// lead); continuation bytes lie in `0x80..0xC0`. Column-counting
211/// therefore matches the pre-lift walk by advancing `col` only on
212/// non-continuation bytes.
213///
214/// Semantic-preserving across every fixture the sibling
215/// [`tests::line_column_handles_newlines`] pins on ASCII input, plus
216/// the added [`tests::line_column_is_const_across_ascii_and_utf8`]
217/// UTF-8 sweep (a 2-byte `é` and a 4-byte `😀` fixture, both of which
218/// the byte-role decision correctly folds onto one column advance per
219/// codepoint start byte, matching the pre-lift `char_indices` walk).
220///
221/// The offset clamp mirrors the pre-lift `break` on `i >= offset`: when
222/// `offset` falls beyond the src end, the walk stops at the last byte
223/// rather than reading past it; when `offset` falls in the middle of a
224/// multi-byte codepoint, the walk stops before the codepoint's start
225/// byte would be counted, matching the pre-lift arm that consumed no
226/// char whose start byte index was `>= offset`.
227#[must_use]
228pub const fn line_column(src: &str, offset: u32) -> Position {
229 let mut line: u32 = 1;
230 let mut col: u32 = 1;
231 let offset = offset as usize;
232 let bytes = src.as_bytes();
233 let end = if offset < bytes.len() {
234 offset
235 } else {
236 bytes.len()
237 };
238 let mut i = 0;
239 while i < end {
240 let b = bytes[i];
241 if b == b'\n' {
242 line += 1;
243 col = 1;
244 } else if b < 0x80 || b >= 0xC0 {
245 // ASCII byte (0x00..0x80) or multi-byte codepoint lead
246 // (0xC0..) — advances the column by one, matching
247 // one char in the pre-lift `char_indices` walk. A
248 // continuation byte (0x80..0xC0) belongs to a codepoint
249 // that has already been counted at its lead byte and
250 // must not re-advance `col`.
251 col += 1;
252 }
253 i += 1;
254 }
255 Position::new(line, col)
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn point_span_is_empty() {
264 let s = Span::point(5);
265 assert!(s.is_empty());
266 assert_eq!(s.len(), 0);
267 }
268
269 #[test]
270 fn union_widens() {
271 let a = Span::new(2, 5);
272 let b = Span::new(4, 9);
273 let u = a.union(b);
274 assert_eq!(u.start, 2);
275 assert_eq!(u.end, 9);
276 }
277
278 #[test]
279 fn union_is_const() {
280 // Pin the const-eval surface: the substrate-primitive parent-span
281 // builder reaches into `const` context, so a future compile-time
282 // parser-fixture / diagnostic-aggregator / fmt-region template
283 // can build a `const OUTER: Span = INNER1.union(INNER2);` without
284 // being forced onto the runtime code path. The four
285 // `const _: () = assert!(…)` bindings resolve the composition at
286 // compile time — any regression that drops `pub const fn` back
287 // to `pub fn` (a body edit that reaches for a non-const
288 // operation) fails this test at compile time rather than at
289 // runtime, matching the sibling `Span::new` / `Span::point` /
290 // `Span::contains` `pub const fn` shape's const-eval discipline.
291 const A: Span = Span::new(2, 5);
292 const B: Span = Span::new(4, 9);
293 const U: Span = A.union(B);
294 const _: () = assert!(U.start == 2);
295 const _: () = assert!(U.end == 9);
296 // Half-open containment on the const-composed parent span keys
297 // through `Span::contains`'s own `pub const fn` promotion — so
298 // both const-eval surfaces (composition and containment) resolve
299 // in one compile-time expression, matching the [start, end)
300 // boundary discipline the sibling `contains_is_const` fixture
301 // already pins.
302 const _: () = assert!(U.contains(2));
303 const _: () = assert!(!U.contains(9));
304 }
305
306 #[test]
307 fn slice_extracts_substring() {
308 let src = "hello world";
309 assert_eq!(Span::new(6, 11).slice(src), "world");
310 }
311
312 #[test]
313 fn line_column_handles_newlines() {
314 let src = "abc\ndef\nghi";
315 assert_eq!(line_column(src, 0), Position::origin());
316 assert_eq!(line_column(src, 4), Position::new(2, 1));
317 assert_eq!(line_column(src, 9), Position::new(3, 2));
318 }
319
320 #[test]
321 fn contains_is_half_open() {
322 let s = Span::new(3, 7);
323 assert!(!s.contains(2));
324 assert!(s.contains(3));
325 assert!(s.contains(6));
326 assert!(!s.contains(7));
327 }
328
329 #[test]
330 fn len_and_is_empty_are_const() {
331 // Pin the const-eval surface: the substrate-primitive width
332 // accessor and emptiness predicate reach into `const` context, so
333 // a future compile-time span-registry / LSP hover-oracle /
334 // trivia-owner truth-table fixture can key off `Span::len` /
335 // `Span::is_empty` without being forced onto the runtime code
336 // path. Any regression that drops `pub const fn` back to `pub fn`
337 // (a body edit that reaches for a non-const operation) fails this
338 // test at compile time rather than at runtime, matching the
339 // sibling `Span::new` / `Span::point` / `Span::contains` /
340 // `Span::union` `pub const fn` shape's const-eval discipline.
341 const RANGE: Span = Span::new(3, 7);
342 const POINT: Span = Span::point(5);
343 const RANGE_LEN: u32 = RANGE.len();
344 const POINT_LEN: u32 = POINT.len();
345 const RANGE_EMPTY: bool = RANGE.is_empty();
346 const POINT_EMPTY: bool = POINT.is_empty();
347 const _: () = assert!(RANGE_LEN == 4);
348 const _: () = assert!(POINT_LEN == 0);
349 const _: () = assert!(!RANGE_EMPTY);
350 const _: () = assert!(POINT_EMPTY);
351 // Saturating-sub floor: an inverted (end < start) fixture must
352 // clamp to 0 at compile time, matching the runtime
353 // `u32::saturating_sub` semantics the pre-lift body carried.
354 const INVERTED: Span = Span::new(9, 2);
355 const INVERTED_LEN: u32 = INVERTED.len();
356 const INVERTED_EMPTY: bool = INVERTED.is_empty();
357 const _: () = assert!(INVERTED_LEN == 0);
358 const _: () = assert!(INVERTED_EMPTY);
359 }
360
361 #[test]
362 fn position_new_and_origin_are_const() {
363 // Pin the const-eval surface on the substrate-primitive
364 // 1-indexed line/column pair: the constructor and canonical
365 // origin reach into `const` context, so a future compile-time
366 // LSP hover-registry / diagnostic-emitter / trivia-owner
367 // truth-table fixture can key off `Position::new` /
368 // `Position::origin` without being forced onto the runtime
369 // code path. Any regression that drops `pub const fn` back to
370 // `pub fn` (a body edit that reaches for a non-const
371 // operation) fails this test at compile time rather than at
372 // runtime, matching the sibling `Span::new` / `Span::point` /
373 // `Span::contains` / `Span::union` / `Span::len` /
374 // `Span::is_empty` `pub const fn` shape's const-eval
375 // discipline on the same caixa-ast source-position primitive
376 // family.
377 //
378 // Also pins `Position::origin`'s canonical (1, 1) shape at
379 // compile time — a future accidental drift (an `origin` that
380 // returns `Position::new(0, 0)` on a well-meaning "zero-
381 // indexed origin" rewrite that forgets `line_column` emits
382 // 1-indexed positions) trips at build time rather than
383 // surfacing far from the origin declaration at some
384 // downstream diagnostic-emitter's off-by-one row report.
385 const AT: Position = Position::new(2, 4);
386 const ORIGIN: Position = Position::origin();
387 const AT_LINE: u32 = AT.line;
388 const AT_COLUMN: u32 = AT.column;
389 const ORIGIN_LINE: u32 = ORIGIN.line;
390 const ORIGIN_COLUMN: u32 = ORIGIN.column;
391 const _: () = assert!(AT_LINE == 2);
392 const _: () = assert!(AT_COLUMN == 4);
393 const _: () = assert!(ORIGIN_LINE == 1);
394 const _: () = assert!(ORIGIN_COLUMN == 1);
395 }
396
397 #[test]
398 fn line_column_is_const_across_ascii_and_utf8() {
399 // Pin the const-eval surface on the last unlifted method of the
400 // caixa-ast source-position primitive family: `line_column`
401 // reaches into `const` context, so a future compile-time
402 // diagnostic-emitter / LSP hover-registry / trivia-owner
403 // truth-table fixture can key off `line_column` without being
404 // forced onto the runtime code path. Any regression that drops
405 // `pub const fn` back to `pub fn` (a body edit that reaches for
406 // a non-const iterator like `str::char_indices`) fails this test
407 // at compile time rather than at runtime, matching the sibling
408 // `Span::new` / `Span::point` / `Span::contains` / `Span::union`
409 // / `Span::len` / `Span::is_empty` / `Position::new` /
410 // `Position::origin` `pub const fn` shape's const-eval discipline
411 // on the same caixa-ast source-position primitive family.
412 //
413 // The ASCII arm matches the sibling `line_column_handles_newlines`
414 // fixture verbatim, resolved at compile time.
415 const ASCII: &str = "abc\ndef\nghi";
416 const ASCII_ORIGIN: Position = line_column(ASCII, 0);
417 const ASCII_ROW_2: Position = line_column(ASCII, 4);
418 const ASCII_ROW_3: Position = line_column(ASCII, 9);
419 const _: () = assert!(ASCII_ORIGIN.line == 1 && ASCII_ORIGIN.column == 1);
420 const _: () = assert!(ASCII_ROW_2.line == 2 && ASCII_ROW_2.column == 1);
421 const _: () = assert!(ASCII_ROW_3.line == 3 && ASCII_ROW_3.column == 2);
422 // Offset clamp: an offset beyond the src end falls onto the last
423 // byte, matching the pre-lift `break` on the exhausted iterator.
424 const ASCII_PAST_END: Position = line_column(ASCII, 100);
425 const _: () = assert!(ASCII_PAST_END.line == 3 && ASCII_PAST_END.column == 4);
426
427 // UTF-8 arm: the byte-role logic (advance `col` only on non-
428 // continuation bytes, `b < 0x80 || b >= 0xC0`) folds every
429 // multi-byte codepoint onto exactly one column advance, matching
430 // the pre-lift `char_indices` walk. `é` is 2 bytes (`0xC3 0xA9`);
431 // `😀` is 4 bytes (`0xF0 0x9F 0x98 0x80`).
432 //
433 // Fixture: "h" (0x68) at byte 0, "é" (0xC3 0xA9) at bytes 1..3,
434 // "l" (0x6C) at byte 3, "l" (0x6C) at byte 4, "o" (0x6F) at byte 5.
435 const UTF8_TWO_BYTE: &str = "héllo";
436 const UTF8_TWO_BYTE_AFTER_H: Position = line_column(UTF8_TWO_BYTE, 1);
437 const UTF8_TWO_BYTE_MID_E_ACUTE: Position = line_column(UTF8_TWO_BYTE, 2);
438 const UTF8_TWO_BYTE_AFTER_E_ACUTE: Position = line_column(UTF8_TWO_BYTE, 3);
439 const UTF8_TWO_BYTE_END: Position = line_column(UTF8_TWO_BYTE, 100);
440 // After "h" (1 codepoint): column 2.
441 const _: () = assert!(UTF8_TWO_BYTE_AFTER_H.line == 1 && UTF8_TWO_BYTE_AFTER_H.column == 2);
442 // Offset 2 falls one byte after `é`'s lead byte (offset 1),
443 // so the codepoint has already been counted at its lead byte
444 // — matches the pre-lift `char_indices` arm that consumed
445 // every char whose start-index was `< offset`. Column 3
446 // = "after h and é" — the mid-codepoint continuation-byte
447 // position resolves to the same 1-indexed column the lead-
448 // byte-adjacent position would.
449 const _: () =
450 assert!(UTF8_TWO_BYTE_MID_E_ACUTE.line == 1 && UTF8_TWO_BYTE_MID_E_ACUTE.column == 3);
451 // After "hé" (2 codepoints): column 3, byte-equal to the mid-
452 // continuation-byte case above — the codepoint boundary at
453 // byte 3 lands on the same 1-indexed column the walk reached
454 // after crossing `é`'s lead byte at byte 1.
455 const _: () = assert!(
456 UTF8_TWO_BYTE_AFTER_E_ACUTE.line == 1 && UTF8_TWO_BYTE_AFTER_E_ACUTE.column == 3
457 );
458 // Past-end clamp on UTF-8 input: 5 codepoints total, column 6.
459 const _: () = assert!(UTF8_TWO_BYTE_END.line == 1 && UTF8_TWO_BYTE_END.column == 6);
460
461 // Fixture: "😀" (0xF0 0x9F 0x98 0x80) at bytes 0..4, space at 4,
462 // "n" at 5.
463 const UTF8_FOUR_BYTE: &str = "😀 nice";
464 const UTF8_FOUR_BYTE_AFTER_EMOJI: Position = line_column(UTF8_FOUR_BYTE, 4);
465 const UTF8_FOUR_BYTE_AFTER_SPACE: Position = line_column(UTF8_FOUR_BYTE, 5);
466 // After "😀" (1 codepoint on a 4-byte sequence): column 2.
467 const _: () =
468 assert!(UTF8_FOUR_BYTE_AFTER_EMOJI.line == 1 && UTF8_FOUR_BYTE_AFTER_EMOJI.column == 2);
469 // After "😀 " (2 codepoints): column 3.
470 const _: () =
471 assert!(UTF8_FOUR_BYTE_AFTER_SPACE.line == 1 && UTF8_FOUR_BYTE_AFTER_SPACE.column == 3);
472
473 // Newline crossing a multi-byte codepoint boundary: "😀\n😀".
474 // Byte layout: 0..4 = 😀, 4 = '\n', 5..9 = 😀. Offset 5 lands at
475 // line 2 column 1 (start of the second codepoint).
476 const UTF8_ACROSS_NEWLINE: &str = "😀\n😀";
477 const UTF8_ACROSS_NEWLINE_ROW_2: Position = line_column(UTF8_ACROSS_NEWLINE, 5);
478 const _: () =
479 assert!(UTF8_ACROSS_NEWLINE_ROW_2.line == 2 && UTF8_ACROSS_NEWLINE_ROW_2.column == 1);
480 }
481
482 #[test]
483 fn contains_is_const() {
484 // Pin the const-eval surface: the substrate-primitive
485 // containment predicate reaches into `const` context, so a
486 // future compile-time span-registry / LSP hover-oracle /
487 // trivia-owner truth-table fixture can key off `Span::contains`
488 // without being forced onto the runtime code path. The four
489 // `const _: () = assert!(…)` bindings resolve the predicate at
490 // compile time — any regression that drops `pub const fn` back
491 // to `pub fn` (a body edit that reaches for a non-const
492 // operation) fails this test at compile time rather than at
493 // runtime, matching the sibling `Span::new` / `Span::point`
494 // `pub const fn` shape's const-eval discipline.
495 const SPAN: Span = Span::new(3, 7);
496 const _: () = assert!(!SPAN.contains(2));
497 const _: () = assert!(SPAN.contains(3));
498 const _: () = assert!(SPAN.contains(5));
499 const _: () = assert!(!SPAN.contains(7));
500 }
501}