1use alloc::{
2 boxed::Box,
3 string::{String, ToString},
4 sync::Arc,
5 vec::Vec,
6};
7use core::{fmt, num::NonZeroU32, ops::Range};
8
9#[cfg(feature = "arbitrary")]
10use proptest::prelude::*;
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use super::{FileLineCol, Position, Selection, SourceId, SourceSpan, Uri};
15
16#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20pub enum SourceLanguage {
21 Masm,
22 Rust,
23 Other(&'static str),
24}
25
26#[cfg(feature = "arbitrary")]
27impl Arbitrary for SourceLanguage {
28 type Parameters = ();
29 type Strategy = BoxedStrategy<Self>;
30
31 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
32 prop_oneof![
33 Just(Self::Masm),
34 Just(Self::Rust),
35 Just(Self::Other("other")),
36 Just(Self::Other("unknown")),
37 ]
38 .boxed()
39 }
40}
41
42impl AsRef<str> for SourceLanguage {
43 fn as_ref(&self) -> &str {
44 match self {
45 Self::Masm => "masm",
46 Self::Rust => "rust",
47 Self::Other(other) => other,
48 }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
57#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
58pub struct SourceFile {
59 id: SourceId,
61 #[cfg_attr(
63 feature = "serde",
64 serde(deserialize_with = "SourceContent::deserialize_and_recompute_line_starts")
65 )]
66 content: SourceContent,
67}
68
69impl miette::SourceCode for SourceFile {
70 fn read_span<'a>(
71 &'a self,
72 span: &miette::SourceSpan,
73 context_lines_before: usize,
74 context_lines_after: usize,
75 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
76 let mut start =
77 u32::try_from(span.offset()).map_err(|_| miette::MietteError::OutOfBounds)?;
78 let len = u32::try_from(span.len()).map_err(|_| miette::MietteError::OutOfBounds)?;
79 let mut end = start.checked_add(len).ok_or(miette::MietteError::OutOfBounds)?;
80 if context_lines_before > 0 {
81 let line_index = self.content.line_index(start.into());
82 let start_line_index = line_index.saturating_sub(context_lines_before as u32);
83 start = self.content.line_start(start_line_index).map(ByteIndex::to_u32).unwrap_or(0);
84 }
85 if context_lines_after > 0 {
86 let line_index = self.content.line_index(end.into());
87 let end_line_index = line_index
88 .checked_add(context_lines_after as u32)
89 .ok_or(miette::MietteError::OutOfBounds)?;
90 end = self
91 .content
92 .line_range(end_line_index)
93 .map(|range| range.end.to_u32())
94 .unwrap_or_else(|| self.content.source_range().end.to_u32());
95 }
96 Ok(Box::new(ScopedSourceFileRef {
97 file: self,
98 span: miette::SourceSpan::new((start as usize).into(), end.abs_diff(start) as usize),
99 }))
100 }
101}
102
103impl SourceFile {
104 pub fn new(id: SourceId, lang: SourceLanguage, uri: Uri, content: impl Into<Box<str>>) -> Self {
106 let content = SourceContent::new(lang, uri, content.into());
107 Self { id, content }
108 }
109
110 pub fn from_raw_parts(id: SourceId, content: SourceContent) -> Self {
127 Self { id, content }
128 }
129
130 pub const fn id(&self) -> SourceId {
132 self.id
133 }
134
135 pub fn uri(&self) -> &Uri {
137 self.content.uri()
138 }
139
140 pub fn content(&self) -> &SourceContent {
142 &self.content
143 }
144
145 pub fn content_mut(&mut self) -> &mut SourceContent {
147 &mut self.content
148 }
149
150 pub fn line_count(&self) -> usize {
152 self.content.line_starts.len()
153 }
154
155 pub fn len(&self) -> usize {
157 self.content.len()
158 }
159
160 pub fn is_empty(&self) -> bool {
162 self.content.is_empty()
163 }
164
165 #[inline(always)]
167 pub fn as_str(&self) -> &str {
168 self.content.as_str()
169 }
170
171 #[inline(always)]
173 pub fn as_bytes(&self) -> &[u8] {
174 self.content.as_bytes()
175 }
176
177 #[inline]
179 pub fn source_span(&self) -> SourceSpan {
180 let range = self.content.source_range();
181 SourceSpan::new(self.id, range.start.0..range.end.0)
182 }
183
184 #[inline(always)]
191 pub fn source_slice(&self, span: impl Into<Range<usize>>) -> Option<&str> {
192 self.content.source_slice(span)
193 }
194
195 pub fn slice(self: &Arc<Self>, span: impl Into<Range<u32>>) -> SourceFileRef {
197 SourceFileRef::new(Arc::clone(self), span)
198 }
199
200 pub fn line_column_to_span(
204 &self,
205 line: LineNumber,
206 column: ColumnNumber,
207 ) -> Option<SourceSpan> {
208 let offset = self.content.line_column_to_offset(line.into(), column.into())?;
209 Some(SourceSpan::at(self.id, offset.0))
210 }
211
212 pub fn location(&self, span: SourceSpan) -> FileLineCol {
214 assert_eq!(span.source_id(), self.id, "mismatched source ids");
215
216 self.content
217 .location(ByteIndex(span.into_range().start))
218 .expect("invalid source span: starting byte is out of bounds")
219 }
220}
221
222impl AsRef<str> for SourceFile {
223 #[inline(always)]
224 fn as_ref(&self) -> &str {
225 self.as_str()
226 }
227}
228
229impl AsRef<[u8]> for SourceFile {
230 #[inline(always)]
231 fn as_ref(&self) -> &[u8] {
232 self.as_bytes()
233 }
234}
235
236#[derive(Debug, Clone)]
246pub struct SourceFileRef {
247 file: Arc<SourceFile>,
248 span: SourceSpan,
249}
250
251impl SourceFileRef {
252 pub fn new(file: Arc<SourceFile>, span: impl Into<Range<u32>>) -> Self {
257 let span = span.into();
258 let end = core::cmp::min(span.end, file.len() as u32);
259 let span = SourceSpan::new(file.id(), span.start..end);
260 Self { file, span }
261 }
262
263 pub fn source_file(&self) -> Arc<SourceFile> {
265 self.file.clone()
266 }
267
268 pub fn uri(&self) -> &Uri {
270 self.file.uri()
271 }
272
273 pub const fn span(&self) -> SourceSpan {
275 self.span
276 }
277
278 pub fn as_str(&self) -> &str {
280 self.file.source_slice(self.span).unwrap()
281 }
282
283 #[inline]
285 pub fn as_bytes(&self) -> &[u8] {
286 self.as_str().as_bytes()
287 }
288
289 pub fn len(&self) -> usize {
292 self.span.len()
293 }
294
295 pub fn is_empty(&self) -> bool {
297 self.len() == 0
298 }
299}
300
301impl Eq for SourceFileRef {}
302
303impl PartialEq for SourceFileRef {
304 fn eq(&self, other: &Self) -> bool {
305 self.as_str() == other.as_str()
306 }
307}
308
309impl Ord for SourceFileRef {
310 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
311 self.as_str().cmp(other.as_str())
312 }
313}
314
315impl PartialOrd for SourceFileRef {
316 #[inline(always)]
317 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
318 Some(self.cmp(other))
319 }
320}
321
322impl core::hash::Hash for SourceFileRef {
323 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
324 self.as_str().hash(state);
325 }
326}
327
328impl AsRef<str> for SourceFileRef {
329 #[inline(always)]
330 fn as_ref(&self) -> &str {
331 self.as_str()
332 }
333}
334
335impl AsRef<[u8]> for SourceFileRef {
336 #[inline(always)]
337 fn as_ref(&self) -> &[u8] {
338 self.as_bytes()
339 }
340}
341
342impl From<&SourceFileRef> for miette::SourceSpan {
343 fn from(source: &SourceFileRef) -> Self {
344 source.span.into()
345 }
346}
347
348struct ScopedSourceFileRef<'a> {
350 file: &'a SourceFile,
351 span: miette::SourceSpan,
352}
353
354impl<'a> miette::SpanContents<'a> for ScopedSourceFileRef<'a> {
355 #[inline]
356 fn data(&self) -> &'a [u8] {
357 let start = self.span.offset();
358 let end = start + self.span.len();
359 &self.file.as_bytes()[start..end]
360 }
361
362 #[inline]
363 fn span(&self) -> &miette::SourceSpan {
364 &self.span
365 }
366
367 fn line(&self) -> usize {
368 let offset = self.span.offset() as u32;
369 self.file.content.line_index(offset.into()).to_usize()
370 }
371
372 fn column(&self) -> usize {
373 let start = self.span.offset() as u32;
374 let end = start + self.span.len() as u32;
375 let span = SourceSpan::new(self.file.id(), start..end);
376 let loc = self.file.location(span);
377 loc.column.to_index().to_usize()
378 }
379
380 #[inline]
381 fn line_count(&self) -> usize {
382 self.file.line_count()
383 }
384
385 #[inline]
386 fn name(&self) -> Option<&str> {
387 Some(self.file.uri().as_ref())
388 }
389
390 #[inline]
391 fn language(&self) -> Option<&str> {
392 None
393 }
394}
395
396impl miette::SourceCode for SourceFileRef {
397 #[inline]
398 fn read_span<'a>(
399 &'a self,
400 span: &miette::SourceSpan,
401 context_lines_before: usize,
402 context_lines_after: usize,
403 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
404 self.file.read_span(span, context_lines_before, context_lines_after)
405 }
406}
407
408#[derive(Clone)]
417#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
418pub struct SourceContent {
419 language: Box<str>,
421 uri: Uri,
423 content: String,
425 #[cfg_attr(feature = "serde", serde(default, skip))]
427 line_starts: Vec<ByteIndex>,
428 #[cfg_attr(feature = "serde", serde(default))]
430 version: i32,
431}
432
433impl fmt::Debug for SourceContent {
434 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
435 let Self {
436 language,
437 uri,
438 content,
439 line_starts,
440 version,
441 } = self;
442 f.debug_struct("SourceContent")
443 .field("version", version)
444 .field("language", language)
445 .field("uri", uri)
446 .field("size_in_bytes", &content.len())
447 .field("line_count", &line_starts.len())
448 .field("content", content)
449 .finish()
450 }
451}
452
453impl Eq for SourceContent {}
454
455impl PartialEq for SourceContent {
456 #[inline]
457 fn eq(&self, other: &Self) -> bool {
458 self.language == other.language && self.uri == other.uri && self.content == other.content
459 }
460}
461
462impl Ord for SourceContent {
463 #[inline]
464 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
465 self.uri.cmp(&other.uri).then_with(|| self.content.cmp(&other.content))
466 }
467}
468
469impl PartialOrd for SourceContent {
470 #[inline]
471 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
472 Some(self.cmp(other))
473 }
474}
475
476impl core::hash::Hash for SourceContent {
477 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
478 self.language.hash(state);
479 self.uri.hash(state);
480 self.content.hash(state);
481 }
482}
483
484#[derive(Debug, thiserror::Error)]
485pub enum SourceContentUpdateError {
486 #[error("invalid content selection: start position of {}:{} is out of bounds", .0.line, .0.character)]
487 InvalidSelectionStart(Position),
488 #[error("invalid content selection: end position of {}:{} is out of bounds", .0.line, .0.character)]
489 InvalidSelectionEnd(Position),
490}
491
492impl SourceContent {
493 pub fn new(language: impl AsRef<str>, uri: impl Into<Uri>, content: impl Into<String>) -> Self {
499 let language = language.as_ref().to_string().into_boxed_str();
500 let content: String = content.into();
501 let bytes = content.as_bytes();
502
503 assert!(
504 bytes.len() < u32::MAX as usize,
505 "unsupported source file: current maximum supported length in bytes is 2^32"
506 );
507
508 let line_starts = compute_line_starts(&content, None);
509
510 Self {
511 language,
512 uri: uri.into(),
513 content,
514 line_starts,
515 version: 0,
516 }
517 }
518
519 pub fn language(&self) -> &str {
521 &self.language
522 }
523
524 pub fn version(&self) -> i32 {
526 self.version
527 }
528
529 #[inline(always)]
531 pub fn set_version(&mut self, version: i32) {
532 self.version = version;
533 }
534
535 #[inline]
537 pub fn uri(&self) -> &Uri {
538 &self.uri
539 }
540
541 #[inline(always)]
543 pub fn as_str(&self) -> &str {
544 self.content.as_ref()
545 }
546
547 #[inline(always)]
549 pub fn as_bytes(&self) -> &[u8] {
550 self.content.as_bytes()
551 }
552
553 #[inline(always)]
555 pub fn len(&self) -> usize {
556 self.content.len()
557 }
558
559 #[inline(always)]
561 pub fn is_empty(&self) -> bool {
562 self.content.is_empty()
563 }
564
565 #[inline]
567 pub fn source_range(&self) -> Range<ByteIndex> {
568 ByteIndex(0)..ByteIndex(self.content.len() as u32)
569 }
570
571 #[inline(always)]
578 pub fn source_slice(&self, span: impl Into<Range<usize>>) -> Option<&str> {
579 self.as_str().get(span.into())
580 }
581
582 #[inline(always)]
586 pub fn byte_slice(&self, span: impl Into<Range<ByteIndex>>) -> Option<&[u8]> {
587 let Range { start, end } = span.into();
588 self.as_bytes().get(start.to_usize()..end.to_usize())
589 }
590
591 pub fn select(&self, mut range: Selection) -> Option<&str> {
596 range.canonicalize();
597
598 let start = self.line_column_to_offset(range.start.line, range.start.character)?;
599 let end = self.line_column_to_offset(range.end.line, range.end.character)?;
600
601 Some(&self.as_str()[start.to_usize()..end.to_usize()])
602 }
603
604 pub fn line_count(&self) -> usize {
606 self.line_starts.len()
607 }
608
609 pub fn line_start(&self, line_index: LineIndex) -> Option<ByteIndex> {
613 self.line_starts.get(line_index.to_usize()).copied()
614 }
615
616 pub fn last_line_index(&self) -> LineIndex {
618 LineIndex(self.line_count().saturating_sub(1).try_into().expect("too many lines in file"))
619 }
620
621 pub fn line_range(&self, line_index: LineIndex) -> Option<Range<ByteIndex>> {
623 let line_start = self.line_start(line_index)?;
624 match self.line_start(line_index + 1) {
625 Some(line_end) => Some(line_start..line_end),
626 None => Some(line_start..ByteIndex(self.content.len() as u32)),
627 }
628 }
629
630 pub fn line_index(&self, byte_index: ByteIndex) -> LineIndex {
632 match self.line_starts.binary_search(&byte_index) {
633 Ok(line) => LineIndex(line as u32),
634 Err(next_line) => LineIndex(next_line as u32 - 1),
635 }
636 }
637
638 pub fn line_column_to_offset(
642 &self,
643 line_index: LineIndex,
644 column_index: ColumnIndex,
645 ) -> Option<ByteIndex> {
646 let column_index = column_index.to_usize();
647 let line_span = self.line_range(line_index)?;
648 let line_src = self
649 .content
650 .get(line_span.start.to_usize()..line_span.end.to_usize())
651 .expect("invalid line boundaries: invalid utf-8");
652 if line_src.len() < column_index {
653 return None;
654 }
655 let (pre, _) = line_src.split_at(column_index);
656 let start = line_span.start;
657 Some(start + ByteOffset::from_str_len(pre))
658 }
659
660 pub fn location(&self, byte_index: ByteIndex) -> Option<FileLineCol> {
663 let line_index = self.line_index(byte_index);
664 let line_start_index = self.line_start(line_index)?;
665 let line_src = self.content.get(line_start_index.to_usize()..byte_index.to_usize())?;
666 let column_index = ColumnIndex::from(line_src.chars().count() as u32);
667 Some(FileLineCol {
668 uri: self.uri.clone(),
669 line: line_index.number(),
670 column: column_index.number(),
671 })
672 }
673
674 pub fn update(
681 &mut self,
682 text: String,
683 range: Option<Selection>,
684 version: i32,
685 ) -> Result<(), SourceContentUpdateError> {
686 match range {
687 Some(range) => {
688 let start = self
689 .line_column_to_offset(range.start.line, range.start.character)
690 .ok_or(SourceContentUpdateError::InvalidSelectionStart(range.start))?
691 .to_usize();
692 let end = self
693 .line_column_to_offset(range.end.line, range.end.character)
694 .ok_or(SourceContentUpdateError::InvalidSelectionEnd(range.end))?
695 .to_usize();
696 assert!(start <= end, "start of range must be less than end, got {start}..{end}",);
697 self.content.replace_range(start..end, &text);
698
699 let added_line_starts = compute_line_starts(&text, Some(start as u32));
700 let num_added = added_line_starts.len();
701 let splice_start = range.start.line.to_usize() + 1;
702 enum Deletion {
707 Empty,
708 Inclusive(usize), }
710 let deletion = if range.start.line == range.end.line {
711 Deletion::Empty
712 } else {
713 let mut end_line_for_splice = range.end.line.to_usize();
714 if !self.line_starts.is_empty() {
715 let max_idx = self.line_starts.len() - 1;
716 if end_line_for_splice > max_idx {
717 end_line_for_splice = max_idx;
718 }
719 }
720 if end_line_for_splice >= splice_start {
721 Deletion::Inclusive(end_line_for_splice)
722 } else {
723 Deletion::Empty
724 }
725 };
726
727 match deletion {
728 Deletion::Empty => {
729 self.line_starts.splice(splice_start..splice_start, added_line_starts);
730 },
731 Deletion::Inclusive(end_idx) => {
732 self.line_starts.splice(splice_start..=end_idx, added_line_starts);
733 },
734 }
735
736 let diff =
737 (text.len() as i32).saturating_sub_unsigned((end as u32) - (start as u32));
738 if diff != 0 {
739 for i in (splice_start + num_added)..self.line_starts.len() {
740 self.line_starts[i] =
741 ByteIndex(self.line_starts[i].to_u32().saturating_add_signed(diff));
742 }
743 }
744 },
745 None => {
746 self.line_starts = compute_line_starts(&text, None);
747 self.content = text;
748 },
749 }
750
751 self.version = version;
752
753 Ok(())
754 }
755}
756
757#[cfg(feature = "serde")]
758impl SourceContent {
759 fn deserialize_and_recompute_line_starts<'de, D>(deserializer: D) -> Result<Self, D::Error>
760 where
761 D: serde::Deserializer<'de>,
762 {
763 let mut content = SourceContent::deserialize(deserializer)?;
764 content.line_starts = compute_line_starts(&content.content, None);
765 Ok(content)
766 }
767}
768
769fn compute_line_starts(text: &str, text_offset: Option<u32>) -> Vec<ByteIndex> {
770 let bytes = text.as_bytes();
771 let initial_line_offset = match text_offset {
772 Some(_) => None,
773 None => Some(ByteIndex(0)),
774 };
775 let text_offset = text_offset.unwrap_or(0);
776 initial_line_offset
777 .into_iter()
778 .chain(
779 memchr::memchr_iter(b'\n', bytes)
780 .map(|offset| ByteIndex(text_offset + (offset + 1) as u32)),
781 )
782 .collect()
783}
784
785#[derive(
790 Default,
791 Debug,
792 Copy,
793 Clone,
794 PartialEq,
795 Eq,
796 PartialOrd,
797 Ord,
798 Hash,
799 zerocopy::FromBytes,
800 zerocopy::Immutable,
801 zerocopy::IntoBytes,
802 zerocopy::KnownLayout,
803)]
804#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
805#[cfg_attr(feature = "serde", serde(transparent))]
806#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
807pub struct ByteIndex(pub u32);
808
809impl ByteIndex {
810 pub const fn new(index: u32) -> Self {
812 Self(index)
813 }
814
815 #[inline(always)]
817 pub const fn to_usize(self) -> usize {
818 self.0 as usize
819 }
820
821 #[inline(always)]
823 pub const fn to_u32(self) -> u32 {
824 self.0
825 }
826}
827
828impl core::ops::Add<ByteOffset> for ByteIndex {
829 type Output = ByteIndex;
830
831 fn add(self, rhs: ByteOffset) -> Self {
832 Self((self.0 as i64 + rhs.0) as u32)
833 }
834}
835
836impl core::ops::Add<u32> for ByteIndex {
837 type Output = ByteIndex;
838
839 fn add(self, rhs: u32) -> Self {
840 Self(self.0 + rhs)
841 }
842}
843
844impl core::ops::AddAssign<ByteOffset> for ByteIndex {
845 fn add_assign(&mut self, rhs: ByteOffset) {
846 *self = *self + rhs;
847 }
848}
849
850impl core::ops::AddAssign<u32> for ByteIndex {
851 fn add_assign(&mut self, rhs: u32) {
852 self.0 += rhs;
853 }
854}
855
856impl core::ops::Sub<ByteOffset> for ByteIndex {
857 type Output = ByteIndex;
858
859 fn sub(self, rhs: ByteOffset) -> Self {
860 Self((self.0 as i64 - rhs.0) as u32)
861 }
862}
863
864impl core::ops::Sub<u32> for ByteIndex {
865 type Output = ByteIndex;
866
867 fn sub(self, rhs: u32) -> Self {
868 Self(self.0 - rhs)
869 }
870}
871
872impl core::ops::SubAssign<ByteOffset> for ByteIndex {
873 fn sub_assign(&mut self, rhs: ByteOffset) {
874 *self = *self - rhs;
875 }
876}
877
878impl core::ops::SubAssign<u32> for ByteIndex {
879 fn sub_assign(&mut self, rhs: u32) {
880 self.0 -= rhs;
881 }
882}
883
884impl From<u32> for ByteIndex {
885 fn from(index: u32) -> Self {
886 Self(index)
887 }
888}
889
890impl From<ByteIndex> for u32 {
891 fn from(index: ByteIndex) -> Self {
892 index.0
893 }
894}
895
896impl fmt::Display for ByteIndex {
897 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
898 fmt::Display::fmt(&self.0, f)
899 }
900}
901
902#[cfg(feature = "arbitrary")]
903impl Arbitrary for ByteIndex {
904 type Parameters = ();
905 type Strategy = BoxedStrategy<Self>;
906
907 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
908 any::<u32>().prop_map(Self).boxed()
909 }
910}
911
912#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
914pub struct ByteOffset(i64);
915
916impl ByteOffset {
917 pub fn from_char_len(c: char) -> ByteOffset {
919 Self(c.len_utf8() as i64)
920 }
921
922 pub fn from_str_len(s: &str) -> ByteOffset {
924 Self(s.len() as i64)
925 }
926}
927
928impl core::ops::Add for ByteOffset {
929 type Output = ByteOffset;
930
931 fn add(self, rhs: Self) -> Self {
932 Self(self.0 + rhs.0)
933 }
934}
935
936impl core::ops::AddAssign for ByteOffset {
937 fn add_assign(&mut self, rhs: Self) {
938 self.0 += rhs.0;
939 }
940}
941
942impl core::ops::Sub for ByteOffset {
943 type Output = ByteOffset;
944
945 fn sub(self, rhs: Self) -> Self {
946 Self(self.0 - rhs.0)
947 }
948}
949
950impl core::ops::SubAssign for ByteOffset {
951 fn sub_assign(&mut self, rhs: Self) {
952 self.0 -= rhs.0;
953 }
954}
955
956impl fmt::Display for ByteOffset {
957 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
958 fmt::Display::fmt(&self.0, f)
959 }
960}
961
962macro_rules! declare_dual_number_and_index_type {
963 ($name:ident, $description:literal) => {
964 paste::paste! {
965 declare_dual_number_and_index_type!([<$name Index>], [<$name Number>], $description);
966 }
967 };
968
969 ($index_name:ident, $number_name:ident, $description:literal) => {
970 #[doc = concat!("A zero-indexed ", $description, " number")]
971 #[derive(
972 Default,
973 Debug,
974 Copy,
975 Clone,
976 PartialEq,
977 Eq,
978 PartialOrd,
979 Ord,
980 Hash,
981 zerocopy::FromBytes,
982 zerocopy::Immutable,
983 zerocopy::IntoBytes,
984 zerocopy::KnownLayout,
985 )]
986 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
987 #[cfg_attr(feature = "serde", serde(transparent))]
988 #[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
989 pub struct $index_name(pub u32);
990
991 impl $index_name {
992 #[doc = concat!("Convert to a [", stringify!($number_name), "]")]
993 pub const fn number(self) -> $number_name {
994 $number_name(unsafe { NonZeroU32::new_unchecked(self.0 + 1) })
995 }
996
997 #[inline(always)]
999 pub const fn to_usize(self) -> usize {
1000 self.0 as usize
1001 }
1002
1003 #[inline(always)]
1005 pub const fn to_u32(self) -> u32 {
1006 self.0
1007 }
1008
1009 pub fn checked_add(self, offset: u32) -> Option<Self> {
1011 self.0.checked_add(offset).map(Self)
1012 }
1013
1014 pub fn checked_add_signed(self, offset: i32) -> Option<Self> {
1016 self.0.checked_add_signed(offset).map(Self)
1017 }
1018
1019 pub fn checked_sub(self, offset: u32) -> Option<Self> {
1021 self.0.checked_sub(offset).map(Self)
1022 }
1023
1024 pub const fn saturating_add(self, offset: u32) -> Self {
1026 Self(self.0.saturating_add(offset))
1027 }
1028
1029 pub const fn saturating_add_signed(self, offset: i32) -> Self {
1032 Self(self.0.saturating_add_signed(offset))
1033 }
1034
1035 pub const fn saturating_sub(self, offset: u32) -> Self {
1037 Self(self.0.saturating_sub(offset))
1038 }
1039 }
1040
1041 impl From<u32> for $index_name {
1042 #[inline]
1043 fn from(index: u32) -> Self {
1044 Self(index)
1045 }
1046 }
1047
1048 impl From<$number_name> for $index_name {
1049 #[inline]
1050 fn from(index: $number_name) -> Self {
1051 Self(index.to_u32() - 1)
1052 }
1053 }
1054
1055 impl core::ops::Add<u32> for $index_name {
1056 type Output = Self;
1057
1058 #[inline]
1059 fn add(self, rhs: u32) -> Self {
1060 Self(self.0 + rhs)
1061 }
1062 }
1063
1064 impl core::ops::AddAssign<u32> for $index_name {
1065 fn add_assign(&mut self, rhs: u32) {
1066 let result = *self + rhs;
1067 *self = result;
1068 }
1069 }
1070
1071 impl core::ops::Add<i32> for $index_name {
1072 type Output = Self;
1073
1074 fn add(self, rhs: i32) -> Self {
1075 self.checked_add_signed(rhs).expect("invalid offset: overflow occurred")
1076 }
1077 }
1078
1079 impl core::ops::AddAssign<i32> for $index_name {
1080 fn add_assign(&mut self, rhs: i32) {
1081 let result = *self + rhs;
1082 *self = result;
1083 }
1084 }
1085
1086 impl core::ops::Sub<u32> for $index_name {
1087 type Output = Self;
1088
1089 #[inline]
1090 fn sub(self, rhs: u32) -> Self {
1091 Self(self.0 - rhs)
1092 }
1093 }
1094
1095 impl core::ops::SubAssign<u32> for $index_name {
1096 fn sub_assign(&mut self, rhs: u32) {
1097 let result = *self - rhs;
1098 *self = result;
1099 }
1100 }
1101
1102 impl fmt::Display for $index_name {
1103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1104 fmt::Display::fmt(&self.0, f)
1105 }
1106 }
1107
1108 #[cfg(feature = "arbitrary")]
1109 impl Arbitrary for $index_name {
1110 type Parameters = ();
1111 type Strategy = BoxedStrategy<Self>;
1112
1113 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1114 any::<u32>().prop_map(Self).boxed()
1115 }
1116 }
1117
1118 #[doc = concat!("A one-indexed ", $description, " number")]
1119 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1120 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1121 #[cfg_attr(feature = "serde", serde(transparent))]
1122 #[cfg_attr(
1123 all(feature = "arbitrary", test),
1124 miden_test_serde_macros::serde_test(binary_serde(true))
1125 )]
1126 pub struct $number_name(NonZeroU32);
1127
1128 impl Default for $number_name {
1129 fn default() -> Self {
1130 Self(unsafe { NonZeroU32::new_unchecked(1) })
1131 }
1132 }
1133
1134 impl $number_name {
1135 pub const fn new(number: u32) -> Option<Self> {
1136 match NonZeroU32::new(number) {
1137 Some(num) => Some(Self(num)),
1138 None => None,
1139 }
1140 }
1141
1142 #[doc = concat!("Convert to a [", stringify!($index_name), "]")]
1143 pub const fn to_index(self) -> $index_name {
1144 $index_name(self.to_u32().saturating_sub(1))
1145 }
1146
1147 #[inline(always)]
1149 pub const fn to_usize(self) -> usize {
1150 self.0.get() as usize
1151 }
1152
1153 #[inline(always)]
1155 pub const fn to_u32(self) -> u32 {
1156 self.0.get()
1157 }
1158
1159 pub fn checked_add(self, offset: u32) -> Option<Self> {
1161 self.0.checked_add(offset).map(Self)
1162 }
1163
1164 pub fn checked_add_signed(self, offset: i32) -> Option<Self> {
1166 self.0.get().checked_add_signed(offset).and_then(Self::new)
1167 }
1168
1169 pub fn checked_sub(self, offset: u32) -> Option<Self> {
1171 self.0.get().checked_sub(offset).and_then(Self::new)
1172 }
1173
1174 pub const fn saturating_add(self, offset: u32) -> Self {
1176 Self(unsafe { NonZeroU32::new_unchecked(self.0.get().saturating_add(offset)) })
1177 }
1178
1179 pub fn saturating_add_signed(self, offset: i32) -> Self {
1182 Self::new(self.to_u32().saturating_add_signed(offset)).unwrap_or_default()
1183 }
1184
1185 pub fn saturating_sub(self, offset: u32) -> Self {
1187 Self::new(self.to_u32().saturating_sub(offset)).unwrap_or_default()
1188 }
1189 }
1190
1191 impl From<NonZeroU32> for $number_name {
1192 #[inline]
1193 fn from(index: NonZeroU32) -> Self {
1194 Self(index)
1195 }
1196 }
1197
1198 impl From<$index_name> for $number_name {
1199 #[inline]
1200 fn from(index: $index_name) -> Self {
1201 Self(unsafe { NonZeroU32::new_unchecked(index.to_u32() + 1) })
1202 }
1203 }
1204
1205 impl core::ops::Add<u32> for $number_name {
1206 type Output = Self;
1207
1208 #[inline]
1209 fn add(self, rhs: u32) -> Self {
1210 Self(unsafe { NonZeroU32::new_unchecked(self.0.get() + rhs) })
1211 }
1212 }
1213
1214 impl core::ops::AddAssign<u32> for $number_name {
1215 fn add_assign(&mut self, rhs: u32) {
1216 let result = *self + rhs;
1217 *self = result;
1218 }
1219 }
1220
1221 impl core::ops::Add<i32> for $number_name {
1222 type Output = Self;
1223
1224 fn add(self, rhs: i32) -> Self {
1225 self.to_u32()
1226 .checked_add_signed(rhs)
1227 .and_then(Self::new)
1228 .expect("invalid offset: overflow occurred")
1229 }
1230 }
1231
1232 impl core::ops::AddAssign<i32> for $number_name {
1233 fn add_assign(&mut self, rhs: i32) {
1234 let result = *self + rhs;
1235 *self = result;
1236 }
1237 }
1238
1239 impl core::ops::Sub<u32> for $number_name {
1240 type Output = Self;
1241
1242 #[inline]
1243 fn sub(self, rhs: u32) -> Self {
1244 self.to_u32()
1245 .checked_sub(rhs)
1246 .and_then(Self::new)
1247 .expect("invalid offset: overflow occurred")
1248 }
1249 }
1250
1251 impl core::ops::SubAssign<u32> for $number_name {
1252 fn sub_assign(&mut self, rhs: u32) {
1253 let result = *self - rhs;
1254 *self = result;
1255 }
1256 }
1257
1258 impl fmt::Display for $number_name {
1259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1260 fmt::Display::fmt(&self.0, f)
1261 }
1262 }
1263 };
1264}
1265
1266declare_dual_number_and_index_type!(Line, "line");
1267declare_dual_number_and_index_type!(Column, "column");
1268
1269use miden_crypto::utils::{
1273 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
1274};
1275
1276impl Serializable for LineNumber {
1277 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1278 target.write_u32(self.to_u32());
1279 }
1280}
1281
1282impl Deserializable for LineNumber {
1283 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1284 let value = source.read_u32()?;
1285 Self::new(value)
1286 .ok_or_else(|| DeserializationError::InvalidValue("line number cannot be zero".into()))
1287 }
1288}
1289
1290impl Serializable for ColumnNumber {
1291 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1292 target.write_u32(self.to_u32());
1293 }
1294}
1295
1296impl Deserializable for ColumnNumber {
1297 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1298 let value = source.read_u32()?;
1299 Self::new(value).ok_or_else(|| {
1300 DeserializationError::InvalidValue("column number cannot be zero".into())
1301 })
1302 }
1303}
1304
1305#[cfg(feature = "arbitrary")]
1306impl Arbitrary for LineNumber {
1307 type Parameters = ();
1308 type Strategy = BoxedStrategy<Self>;
1309
1310 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1311 (1..=u32::MAX)
1312 .prop_map(|value| Self::new(value).expect("non-zero value"))
1313 .boxed()
1314 }
1315}
1316
1317#[cfg(feature = "arbitrary")]
1318impl Arbitrary for ColumnNumber {
1319 type Parameters = ();
1320 type Strategy = BoxedStrategy<Self>;
1321
1322 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1323 (1..=u32::MAX)
1324 .prop_map(|value| Self::new(value).expect("non-zero value"))
1325 .boxed()
1326 }
1327}
1328
1329#[cfg(test)]
1330mod tests {
1331 use super::*;
1332
1333 #[test]
1334 fn source_content_line_starts() {
1335 const CONTENT: &str = "\
1336begin
1337 push.1
1338 push.2
1339 add
1340end
1341";
1342 let content = SourceContent::new("masm", "foo.masm", CONTENT);
1343
1344 assert_eq!(content.line_count(), 6);
1345 assert_eq!(
1346 content
1347 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1348 .expect("invalid byte range"),
1349 "begin\n".as_bytes()
1350 );
1351 assert_eq!(
1352 content
1353 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1354 .expect("invalid byte range"),
1355 " push.1\n".as_bytes()
1356 );
1357 assert_eq!(
1358 content
1359 .byte_slice(content.line_range(content.last_line_index()).expect("invalid line"))
1360 .expect("invalid byte range"),
1361 "".as_bytes()
1362 );
1363 }
1364
1365 #[test]
1366 fn source_content_line_starts_after_update() {
1367 const CONTENT: &str = "\
1368begin
1369 push.1
1370 push.2
1371 add
1372end
1373";
1374 const FRAGMENT: &str = " push.2
1375 mul
1376end
1377";
1378 let mut content = SourceContent::new("masm", "foo.masm", CONTENT);
1379 content
1380 .update(FRAGMENT.to_string(), Some(Selection::from(LineIndex(4)..LineIndex(5))), 1)
1381 .expect("update failed");
1382
1383 assert_eq!(
1384 content.as_str(),
1385 "\
1386begin
1387 push.1
1388 push.2
1389 add
1390 push.2
1391 mul
1392end
1393"
1394 );
1395 assert_eq!(content.line_count(), 8);
1396 assert_eq!(
1397 content
1398 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1399 .expect("invalid byte range"),
1400 "begin\n".as_bytes()
1401 );
1402 assert_eq!(
1403 content
1404 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1405 .expect("invalid byte range"),
1406 " add\n".as_bytes()
1407 );
1408 assert_eq!(
1409 content
1410 .byte_slice(content.line_range(LineIndex(4)).expect("invalid line"))
1411 .expect("invalid byte range"),
1412 " push.2\n".as_bytes()
1413 );
1414 assert_eq!(
1415 content
1416 .byte_slice(content.line_range(content.last_line_index()).expect("invalid line"))
1417 .expect("invalid byte range"),
1418 "".as_bytes()
1419 );
1420 }
1421
1422 #[test]
1424 fn source_content_line_starts_with_trailing_backslash() {
1425 const CONTENT: &str =
1426 "//! Build with:\n//! cargo build \\\n//! --release\nfn main() {}\n";
1427
1428 let content = SourceContent::new("rust", "example.rs", CONTENT);
1429
1430 assert_eq!(content.line_count(), 5);
1437
1438 assert_eq!(
1440 content
1441 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1442 .expect("invalid byte range"),
1443 "//! Build with:\n".as_bytes()
1444 );
1445 assert_eq!(
1446 content
1447 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1448 .expect("invalid byte range"),
1449 "//! cargo build \\\n".as_bytes()
1450 );
1451 assert_eq!(
1452 content
1453 .byte_slice(content.line_range(LineIndex(2)).expect("invalid line"))
1454 .expect("invalid byte range"),
1455 "//! --release\n".as_bytes()
1456 );
1457 assert_eq!(
1458 content
1459 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1460 .expect("invalid byte range"),
1461 "fn main() {}\n".as_bytes()
1462 );
1463
1464 let offset_line0 = content.line_column_to_offset(LineIndex(0), ColumnIndex(0));
1467 let offset_line1 = content.line_column_to_offset(LineIndex(1), ColumnIndex(0));
1468 let offset_line2 = content.line_column_to_offset(LineIndex(2), ColumnIndex(0));
1469 let offset_line3 = content.line_column_to_offset(LineIndex(3), ColumnIndex(0));
1470
1471 assert!(offset_line0.is_some(), "line 0 should be accessible");
1472 assert!(offset_line1.is_some(), "line 1 should be accessible");
1473 assert!(offset_line2.is_some(), "line 2 should be accessible");
1474 assert!(offset_line3.is_some(), "line 3 should be accessible");
1475
1476 assert_eq!(offset_line0.unwrap().to_u32(), 0);
1478 assert_eq!(offset_line1.unwrap().to_u32(), 16); assert_eq!(offset_line2.unwrap().to_u32(), 36); assert_eq!(offset_line3.unwrap().to_u32(), 54); }
1482
1483 #[test]
1485 fn source_content_line_starts_multiple_trailing_backslashes() {
1486 const CONTENT: &str = "line1 \\\nline2 \\\nline3 \\\nline4\n";
1488
1489 let content = SourceContent::new("text", "test.txt", CONTENT);
1490
1491 assert_eq!(content.line_count(), 5);
1493
1494 assert_eq!(
1496 content
1497 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1498 .expect("invalid byte range"),
1499 "line1 \\\n".as_bytes()
1500 );
1501 assert_eq!(
1502 content
1503 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1504 .expect("invalid byte range"),
1505 "line2 \\\n".as_bytes()
1506 );
1507 assert_eq!(
1508 content
1509 .byte_slice(content.line_range(LineIndex(2)).expect("invalid line"))
1510 .expect("invalid byte range"),
1511 "line3 \\\n".as_bytes()
1512 );
1513 assert_eq!(
1514 content
1515 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1516 .expect("invalid byte range"),
1517 "line4\n".as_bytes()
1518 );
1519 }
1520}