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::{
15 ByteReader, ByteWriter, Deserializable, DeserializationError, FileLineCol, Position, Selection,
16 Serializable, SourceId, SourceSpan, Uri,
17};
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23pub enum SourceLanguage {
24 Masm,
25 Rust,
26 Other(&'static str),
27}
28
29#[cfg(feature = "arbitrary")]
30impl Arbitrary for SourceLanguage {
31 type Parameters = ();
32 type Strategy = BoxedStrategy<Self>;
33
34 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
35 prop_oneof![
36 Just(Self::Masm),
37 Just(Self::Rust),
38 Just(Self::Other("other")),
39 Just(Self::Other("unknown")),
40 ]
41 .boxed()
42 }
43}
44
45impl AsRef<str> for SourceLanguage {
46 fn as_ref(&self) -> &str {
47 match self {
48 Self::Masm => "masm",
49 Self::Rust => "rust",
50 Self::Other(other) => other,
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
60#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
61pub struct SourceFile {
62 id: SourceId,
64 #[cfg_attr(
66 feature = "serde",
67 serde(deserialize_with = "SourceContent::deserialize_and_recompute_line_starts")
68 )]
69 content: SourceContent,
70}
71
72impl miette::SourceCode for SourceFile {
73 fn read_span<'a>(
74 &'a self,
75 span: &miette::SourceSpan,
76 context_lines_before: usize,
77 context_lines_after: usize,
78 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
79 let mut start =
80 u32::try_from(span.offset()).map_err(|_| miette::MietteError::OutOfBounds)?;
81 let len = u32::try_from(span.len()).map_err(|_| miette::MietteError::OutOfBounds)?;
82 let mut end = start.checked_add(len).ok_or(miette::MietteError::OutOfBounds)?;
83 if context_lines_before > 0 {
84 let line_index = self.content.line_index(start.into());
85 let start_line_index = line_index.saturating_sub(context_lines_before as u32);
86 start = self.content.line_start(start_line_index).map(ByteIndex::to_u32).unwrap_or(0);
87 }
88 if context_lines_after > 0 {
89 let line_index = self.content.line_index(end.into());
90 let end_line_index = line_index
91 .checked_add(context_lines_after as u32)
92 .ok_or(miette::MietteError::OutOfBounds)?;
93 end = self
94 .content
95 .line_range(end_line_index)
96 .map(|range| range.end.to_u32())
97 .unwrap_or_else(|| self.content.source_range().end.to_u32());
98 }
99 Ok(Box::new(ScopedSourceFileRef {
100 file: self,
101 span: miette::SourceSpan::new((start as usize).into(), end.abs_diff(start) as usize),
102 }))
103 }
104}
105
106impl SourceFile {
107 pub fn new(id: SourceId, lang: SourceLanguage, uri: Uri, content: impl Into<Box<str>>) -> Self {
109 let content = SourceContent::new(lang, uri, content.into());
110 Self { id, content }
111 }
112
113 pub fn from_raw_parts(id: SourceId, content: SourceContent) -> Self {
130 Self { id, content }
131 }
132
133 pub const fn id(&self) -> SourceId {
135 self.id
136 }
137
138 pub fn uri(&self) -> &Uri {
140 self.content.uri()
141 }
142
143 pub fn content(&self) -> &SourceContent {
145 &self.content
146 }
147
148 pub fn content_mut(&mut self) -> &mut SourceContent {
150 &mut self.content
151 }
152
153 pub fn line_count(&self) -> usize {
155 self.content.line_starts.len()
156 }
157
158 pub fn len(&self) -> usize {
160 self.content.len()
161 }
162
163 pub fn is_empty(&self) -> bool {
165 self.content.is_empty()
166 }
167
168 #[inline(always)]
170 pub fn as_str(&self) -> &str {
171 self.content.as_str()
172 }
173
174 #[inline(always)]
176 pub fn as_bytes(&self) -> &[u8] {
177 self.content.as_bytes()
178 }
179
180 #[inline]
182 pub fn source_span(&self) -> SourceSpan {
183 let range = self.content.source_range();
184 SourceSpan::new(self.id, range.start.0..range.end.0)
185 }
186
187 #[inline(always)]
194 pub fn source_slice(&self, span: impl Into<Range<usize>>) -> Option<&str> {
195 self.content.source_slice(span)
196 }
197
198 pub fn slice(self: &Arc<Self>, span: impl Into<Range<u32>>) -> SourceFileRef {
200 SourceFileRef::new(Arc::clone(self), span)
201 }
202
203 pub fn line_column_to_span(
207 &self,
208 line: LineNumber,
209 column: ColumnNumber,
210 ) -> Option<SourceSpan> {
211 let offset = self.content.line_column_to_offset(line.into(), column.into())?;
212 Some(SourceSpan::at(self.id, offset.0))
213 }
214
215 pub fn location(&self, span: SourceSpan) -> FileLineCol {
217 assert_eq!(span.source_id(), self.id, "mismatched source ids");
218
219 self.content
220 .location(ByteIndex(span.into_range().start))
221 .expect("invalid source span: starting byte is out of bounds")
222 }
223}
224
225impl AsRef<str> for SourceFile {
226 #[inline(always)]
227 fn as_ref(&self) -> &str {
228 self.as_str()
229 }
230}
231
232impl AsRef<[u8]> for SourceFile {
233 #[inline(always)]
234 fn as_ref(&self) -> &[u8] {
235 self.as_bytes()
236 }
237}
238
239#[derive(Debug, Clone)]
249pub struct SourceFileRef {
250 file: Arc<SourceFile>,
251 span: SourceSpan,
252}
253
254impl SourceFileRef {
255 pub fn new(file: Arc<SourceFile>, span: impl Into<Range<u32>>) -> Self {
260 let span = span.into();
261 let end = core::cmp::min(span.end, file.len() as u32);
262 let span = SourceSpan::new(file.id(), span.start..end);
263 Self { file, span }
264 }
265
266 pub fn source_file(&self) -> Arc<SourceFile> {
268 self.file.clone()
269 }
270
271 pub fn uri(&self) -> &Uri {
273 self.file.uri()
274 }
275
276 pub const fn span(&self) -> SourceSpan {
278 self.span
279 }
280
281 pub fn as_str(&self) -> &str {
283 self.file.source_slice(self.span).unwrap()
284 }
285
286 #[inline]
288 pub fn as_bytes(&self) -> &[u8] {
289 self.as_str().as_bytes()
290 }
291
292 pub fn len(&self) -> usize {
295 self.span.len()
296 }
297
298 pub fn is_empty(&self) -> bool {
300 self.len() == 0
301 }
302}
303
304impl Eq for SourceFileRef {}
305
306impl PartialEq for SourceFileRef {
307 fn eq(&self, other: &Self) -> bool {
308 self.as_str() == other.as_str()
309 }
310}
311
312impl Ord for SourceFileRef {
313 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
314 self.as_str().cmp(other.as_str())
315 }
316}
317
318impl PartialOrd for SourceFileRef {
319 #[inline(always)]
320 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
321 Some(self.cmp(other))
322 }
323}
324
325impl core::hash::Hash for SourceFileRef {
326 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
327 self.as_str().hash(state);
328 }
329}
330
331impl AsRef<str> for SourceFileRef {
332 #[inline(always)]
333 fn as_ref(&self) -> &str {
334 self.as_str()
335 }
336}
337
338impl AsRef<[u8]> for SourceFileRef {
339 #[inline(always)]
340 fn as_ref(&self) -> &[u8] {
341 self.as_bytes()
342 }
343}
344
345impl From<&SourceFileRef> for miette::SourceSpan {
346 fn from(source: &SourceFileRef) -> Self {
347 source.span.into()
348 }
349}
350
351struct ScopedSourceFileRef<'a> {
353 file: &'a SourceFile,
354 span: miette::SourceSpan,
355}
356
357impl<'a> miette::SpanContents<'a> for ScopedSourceFileRef<'a> {
358 #[inline]
359 fn data(&self) -> &'a [u8] {
360 let start = self.span.offset();
361 let end = start + self.span.len();
362 &self.file.as_bytes()[start..end]
363 }
364
365 #[inline]
366 fn span(&self) -> &miette::SourceSpan {
367 &self.span
368 }
369
370 fn line(&self) -> usize {
371 let offset = self.span.offset() as u32;
372 self.file.content.line_index(offset.into()).to_usize()
373 }
374
375 fn column(&self) -> usize {
376 let start = self.span.offset() as u32;
377 let end = start + self.span.len() as u32;
378 let span = SourceSpan::new(self.file.id(), start..end);
379 let loc = self.file.location(span);
380 loc.column.to_index().to_usize()
381 }
382
383 #[inline]
384 fn line_count(&self) -> usize {
385 self.file.line_count()
386 }
387
388 #[inline]
389 fn name(&self) -> Option<&str> {
390 Some(self.file.uri().as_ref())
391 }
392
393 #[inline]
394 fn language(&self) -> Option<&str> {
395 None
396 }
397}
398
399impl miette::SourceCode for SourceFileRef {
400 #[inline]
401 fn read_span<'a>(
402 &'a self,
403 span: &miette::SourceSpan,
404 context_lines_before: usize,
405 context_lines_after: usize,
406 ) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
407 self.file.read_span(span, context_lines_before, context_lines_after)
408 }
409}
410
411#[derive(Clone)]
420#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
421pub struct SourceContent {
422 language: Box<str>,
424 uri: Uri,
426 content: String,
428 #[cfg_attr(feature = "serde", serde(default, skip))]
430 line_starts: Vec<ByteIndex>,
431 #[cfg_attr(feature = "serde", serde(default))]
433 version: i32,
434}
435
436impl fmt::Debug for SourceContent {
437 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
438 let Self {
439 language,
440 uri,
441 content,
442 line_starts,
443 version,
444 } = self;
445 f.debug_struct("SourceContent")
446 .field("version", version)
447 .field("language", language)
448 .field("uri", uri)
449 .field("size_in_bytes", &content.len())
450 .field("line_count", &line_starts.len())
451 .field("content", content)
452 .finish()
453 }
454}
455
456impl Eq for SourceContent {}
457
458impl PartialEq for SourceContent {
459 #[inline]
460 fn eq(&self, other: &Self) -> bool {
461 self.language == other.language && self.uri == other.uri && self.content == other.content
462 }
463}
464
465impl Ord for SourceContent {
466 #[inline]
467 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
468 self.uri.cmp(&other.uri).then_with(|| self.content.cmp(&other.content))
469 }
470}
471
472impl PartialOrd for SourceContent {
473 #[inline]
474 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
475 Some(self.cmp(other))
476 }
477}
478
479impl core::hash::Hash for SourceContent {
480 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
481 self.language.hash(state);
482 self.uri.hash(state);
483 self.content.hash(state);
484 }
485}
486
487#[derive(Debug, thiserror::Error)]
488pub enum SourceContentUpdateError {
489 #[error("invalid content selection: start position of {}:{} is out of bounds", .0.line, .0.character)]
490 InvalidSelectionStart(Position),
491 #[error("invalid content selection: end position of {}:{} is out of bounds", .0.line, .0.character)]
492 InvalidSelectionEnd(Position),
493}
494
495impl SourceContent {
496 pub fn new(language: impl AsRef<str>, uri: impl Into<Uri>, content: impl Into<String>) -> Self {
502 let language = language.as_ref().to_string().into_boxed_str();
503 let content: String = content.into();
504 let bytes = content.as_bytes();
505
506 assert!(
507 bytes.len() < u32::MAX as usize,
508 "unsupported source file: current maximum supported length in bytes is 2^32"
509 );
510
511 let line_starts = compute_line_starts(&content, None);
512
513 Self {
514 language,
515 uri: uri.into(),
516 content,
517 line_starts,
518 version: 0,
519 }
520 }
521
522 pub fn language(&self) -> &str {
524 &self.language
525 }
526
527 pub fn version(&self) -> i32 {
529 self.version
530 }
531
532 #[inline(always)]
534 pub fn set_version(&mut self, version: i32) {
535 self.version = version;
536 }
537
538 #[inline]
540 pub fn uri(&self) -> &Uri {
541 &self.uri
542 }
543
544 #[inline(always)]
546 pub fn as_str(&self) -> &str {
547 self.content.as_ref()
548 }
549
550 #[inline(always)]
552 pub fn as_bytes(&self) -> &[u8] {
553 self.content.as_bytes()
554 }
555
556 #[inline(always)]
558 pub fn len(&self) -> usize {
559 self.content.len()
560 }
561
562 #[inline(always)]
564 pub fn is_empty(&self) -> bool {
565 self.content.is_empty()
566 }
567
568 #[inline]
570 pub fn source_range(&self) -> Range<ByteIndex> {
571 ByteIndex(0)..ByteIndex(self.content.len() as u32)
572 }
573
574 #[inline(always)]
581 pub fn source_slice(&self, span: impl Into<Range<usize>>) -> Option<&str> {
582 self.as_str().get(span.into())
583 }
584
585 #[inline(always)]
589 pub fn byte_slice(&self, span: impl Into<Range<ByteIndex>>) -> Option<&[u8]> {
590 let Range { start, end } = span.into();
591 self.as_bytes().get(start.to_usize()..end.to_usize())
592 }
593
594 pub fn select(&self, mut range: Selection) -> Option<&str> {
599 range.canonicalize();
600
601 let start = self.line_column_to_offset(range.start.line, range.start.character)?;
602 let end = self.line_column_to_offset(range.end.line, range.end.character)?;
603
604 Some(&self.as_str()[start.to_usize()..end.to_usize()])
605 }
606
607 pub fn line_count(&self) -> usize {
609 self.line_starts.len()
610 }
611
612 pub fn line_start(&self, line_index: LineIndex) -> Option<ByteIndex> {
616 self.line_starts.get(line_index.to_usize()).copied()
617 }
618
619 pub fn last_line_index(&self) -> LineIndex {
621 LineIndex(self.line_count().saturating_sub(1).try_into().expect("too many lines in file"))
622 }
623
624 pub fn line_range(&self, line_index: LineIndex) -> Option<Range<ByteIndex>> {
626 let line_start = self.line_start(line_index)?;
627 match self.line_start(line_index + 1) {
628 Some(line_end) => Some(line_start..line_end),
629 None => Some(line_start..ByteIndex(self.content.len() as u32)),
630 }
631 }
632
633 pub fn line_index(&self, byte_index: ByteIndex) -> LineIndex {
635 match self.line_starts.binary_search(&byte_index) {
636 Ok(line) => LineIndex(line as u32),
637 Err(next_line) => LineIndex(next_line as u32 - 1),
638 }
639 }
640
641 pub fn line_column_to_offset(
648 &self,
649 line_index: LineIndex,
650 column_index: ColumnIndex,
651 ) -> Option<ByteIndex> {
652 let column_index = column_index.to_usize();
653 let line_span = self.line_range(line_index)?;
654 let line_src = self
655 .content
656 .get(line_span.start.to_usize()..line_span.end.to_usize())
657 .expect("invalid line boundaries: invalid utf-8");
658
659 let content = line_src
660 .strip_suffix("\r\n")
661 .or_else(|| line_src.strip_suffix('\n'))
662 .unwrap_or(line_src);
663
664 let byte_len = content
666 .char_indices()
667 .map(|(offset, _)| offset)
668 .chain(core::iter::once(content.len()))
669 .nth(column_index)?;
670
671 Some(line_span.start + ByteOffset(byte_len as i64))
672 }
673
674 pub fn location(&self, byte_index: ByteIndex) -> Option<FileLineCol> {
677 let line_index = self.line_index(byte_index);
678 let line_start_index = self.line_start(line_index)?;
679 let line_src = self.content.get(line_start_index.to_usize()..byte_index.to_usize())?;
680 let column_index = ColumnIndex::from(line_src.chars().count() as u32);
681 Some(FileLineCol {
682 uri: self.uri.clone(),
683 line: line_index.number(),
684 column: column_index.number(),
685 })
686 }
687
688 pub fn update(
695 &mut self,
696 text: String,
697 range: Option<Selection>,
698 version: i32,
699 ) -> Result<(), SourceContentUpdateError> {
700 match range {
701 Some(range) => {
702 let start = self
703 .line_column_to_offset(range.start.line, range.start.character)
704 .ok_or(SourceContentUpdateError::InvalidSelectionStart(range.start))?
705 .to_usize();
706 let end = self
707 .line_column_to_offset(range.end.line, range.end.character)
708 .ok_or(SourceContentUpdateError::InvalidSelectionEnd(range.end))?
709 .to_usize();
710 assert!(start <= end, "start of range must be less than end, got {start}..{end}",);
711 self.content.replace_range(start..end, &text);
712
713 let added_line_starts = compute_line_starts(&text, Some(start as u32));
714 let num_added = added_line_starts.len();
715 let splice_start = range.start.line.to_usize() + 1;
716 enum Deletion {
721 Empty,
722 Inclusive(usize), }
724 let deletion = if range.start.line == range.end.line {
725 Deletion::Empty
726 } else {
727 let mut end_line_for_splice = range.end.line.to_usize();
728 if !self.line_starts.is_empty() {
729 let max_idx = self.line_starts.len() - 1;
730 if end_line_for_splice > max_idx {
731 end_line_for_splice = max_idx;
732 }
733 }
734 if end_line_for_splice >= splice_start {
735 Deletion::Inclusive(end_line_for_splice)
736 } else {
737 Deletion::Empty
738 }
739 };
740
741 match deletion {
742 Deletion::Empty => {
743 self.line_starts.splice(splice_start..splice_start, added_line_starts);
744 },
745 Deletion::Inclusive(end_idx) => {
746 self.line_starts.splice(splice_start..=end_idx, added_line_starts);
747 },
748 }
749
750 let diff =
751 (text.len() as i32).saturating_sub_unsigned((end as u32) - (start as u32));
752 if diff != 0 {
753 for i in (splice_start + num_added)..self.line_starts.len() {
754 self.line_starts[i] =
755 ByteIndex(self.line_starts[i].to_u32().saturating_add_signed(diff));
756 }
757 }
758 },
759 None => {
760 self.line_starts = compute_line_starts(&text, None);
761 self.content = text;
762 },
763 }
764
765 self.version = version;
766
767 Ok(())
768 }
769}
770
771#[cfg(feature = "serde")]
772impl SourceContent {
773 fn deserialize_and_recompute_line_starts<'de, D>(deserializer: D) -> Result<Self, D::Error>
774 where
775 D: serde::Deserializer<'de>,
776 {
777 let mut content = SourceContent::deserialize(deserializer)?;
778 content.line_starts = compute_line_starts(&content.content, None);
779 Ok(content)
780 }
781}
782
783fn compute_line_starts(text: &str, text_offset: Option<u32>) -> Vec<ByteIndex> {
784 let bytes = text.as_bytes();
785 let initial_line_offset = match text_offset {
786 Some(_) => None,
787 None => Some(ByteIndex(0)),
788 };
789 let text_offset = text_offset.unwrap_or(0);
790 initial_line_offset
791 .into_iter()
792 .chain(
793 memchr::memchr_iter(b'\n', bytes)
794 .map(|offset| ByteIndex(text_offset + (offset + 1) as u32)),
795 )
796 .collect()
797}
798
799#[derive(
804 Default,
805 Debug,
806 Copy,
807 Clone,
808 PartialEq,
809 Eq,
810 PartialOrd,
811 Ord,
812 Hash,
813 zerocopy::FromBytes,
814 zerocopy::Immutable,
815 zerocopy::IntoBytes,
816 zerocopy::KnownLayout,
817)]
818#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
819#[cfg_attr(feature = "serde", serde(transparent))]
820#[cfg_attr(
821 all(feature = "arbitrary", test),
822 miden_test_serialization_macros::serialization_test
823)]
824pub struct ByteIndex(pub u32);
825
826impl Serializable for ByteIndex {
827 fn write_into<W: ByteWriter>(&self, target: &mut W) {
828 self.0.write_into(target);
829 }
830}
831
832impl Deserializable for ByteIndex {
833 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
834 u32::read_from(source).map(Self)
835 }
836}
837
838impl ByteIndex {
839 pub const fn new(index: u32) -> Self {
841 Self(index)
842 }
843
844 #[inline(always)]
846 pub const fn to_usize(self) -> usize {
847 self.0 as usize
848 }
849
850 #[inline(always)]
852 pub const fn to_u32(self) -> u32 {
853 self.0
854 }
855}
856
857impl core::ops::Add<ByteOffset> for ByteIndex {
858 type Output = ByteIndex;
859
860 fn add(self, rhs: ByteOffset) -> Self {
861 Self((self.0 as i64 + rhs.0) as u32)
862 }
863}
864
865impl core::ops::Add<u32> for ByteIndex {
866 type Output = ByteIndex;
867
868 fn add(self, rhs: u32) -> Self {
869 Self(self.0 + rhs)
870 }
871}
872
873impl core::ops::AddAssign<ByteOffset> for ByteIndex {
874 fn add_assign(&mut self, rhs: ByteOffset) {
875 *self = *self + rhs;
876 }
877}
878
879impl core::ops::AddAssign<u32> for ByteIndex {
880 fn add_assign(&mut self, rhs: u32) {
881 self.0 += rhs;
882 }
883}
884
885impl core::ops::Sub<ByteOffset> for ByteIndex {
886 type Output = ByteIndex;
887
888 fn sub(self, rhs: ByteOffset) -> Self {
889 Self((self.0 as i64 - rhs.0) as u32)
890 }
891}
892
893impl core::ops::Sub<u32> for ByteIndex {
894 type Output = ByteIndex;
895
896 fn sub(self, rhs: u32) -> Self {
897 Self(self.0 - rhs)
898 }
899}
900
901impl core::ops::SubAssign<ByteOffset> for ByteIndex {
902 fn sub_assign(&mut self, rhs: ByteOffset) {
903 *self = *self - rhs;
904 }
905}
906
907impl core::ops::SubAssign<u32> for ByteIndex {
908 fn sub_assign(&mut self, rhs: u32) {
909 self.0 -= rhs;
910 }
911}
912
913impl From<u32> for ByteIndex {
914 fn from(index: u32) -> Self {
915 Self(index)
916 }
917}
918
919impl From<ByteIndex> for u32 {
920 fn from(index: ByteIndex) -> Self {
921 index.0
922 }
923}
924
925impl fmt::Display for ByteIndex {
926 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
927 fmt::Display::fmt(&self.0, f)
928 }
929}
930
931#[cfg(feature = "arbitrary")]
932impl Arbitrary for ByteIndex {
933 type Parameters = ();
934 type Strategy = BoxedStrategy<Self>;
935
936 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
937 any::<u32>().prop_map(Self).boxed()
938 }
939}
940
941#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
943pub struct ByteOffset(i64);
944
945impl ByteOffset {
946 pub fn from_char_len(c: char) -> ByteOffset {
948 Self(c.len_utf8() as i64)
949 }
950
951 pub fn from_str_len(s: &str) -> ByteOffset {
953 Self(s.len() as i64)
954 }
955}
956
957impl core::ops::Add for ByteOffset {
958 type Output = ByteOffset;
959
960 fn add(self, rhs: Self) -> Self {
961 Self(self.0 + rhs.0)
962 }
963}
964
965impl core::ops::AddAssign for ByteOffset {
966 fn add_assign(&mut self, rhs: Self) {
967 self.0 += rhs.0;
968 }
969}
970
971impl core::ops::Sub for ByteOffset {
972 type Output = ByteOffset;
973
974 fn sub(self, rhs: Self) -> Self {
975 Self(self.0 - rhs.0)
976 }
977}
978
979impl core::ops::SubAssign for ByteOffset {
980 fn sub_assign(&mut self, rhs: Self) {
981 self.0 -= rhs.0;
982 }
983}
984
985impl fmt::Display for ByteOffset {
986 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
987 fmt::Display::fmt(&self.0, f)
988 }
989}
990
991macro_rules! declare_dual_number_and_index_type {
992 ($name:ident, $description:literal) => {
993 paste::paste! {
994 declare_dual_number_and_index_type!([<$name Index>], [<$name Number>], $description);
995 }
996 };
997
998 ($index_name:ident, $number_name:ident, $description:literal) => {
999 #[doc = concat!("A zero-indexed ", $description, " number")]
1000 #[derive(
1001 Default,
1002 Debug,
1003 Copy,
1004 Clone,
1005 PartialEq,
1006 Eq,
1007 PartialOrd,
1008 Ord,
1009 Hash,
1010 zerocopy::FromBytes,
1011 zerocopy::Immutable,
1012 zerocopy::IntoBytes,
1013 zerocopy::KnownLayout,
1014 )]
1015 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1016 #[cfg_attr(feature = "serde", serde(transparent))]
1017 #[cfg_attr(
1018 all(feature = "arbitrary", test),
1019 miden_test_serialization_macros::serialization_test
1020 )]
1021 pub struct $index_name(pub u32);
1022
1023 impl Serializable for $index_name {
1024 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1025 self.0.write_into(target);
1026 }
1027 }
1028
1029 impl Deserializable for $index_name {
1030 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1031 u32::read_from(source).map(Self)
1032 }
1033 }
1034
1035 impl $index_name {
1036 #[doc = concat!("Convert to a [", stringify!($number_name), "]")]
1037 pub const fn number(self) -> $number_name {
1038 $number_name(unsafe { NonZeroU32::new_unchecked(self.0 + 1) })
1039 }
1040
1041 #[inline(always)]
1043 pub const fn to_usize(self) -> usize {
1044 self.0 as usize
1045 }
1046
1047 #[inline(always)]
1049 pub const fn to_u32(self) -> u32 {
1050 self.0
1051 }
1052
1053 pub fn checked_add(self, offset: u32) -> Option<Self> {
1055 self.0.checked_add(offset).map(Self)
1056 }
1057
1058 pub fn checked_add_signed(self, offset: i32) -> Option<Self> {
1060 self.0.checked_add_signed(offset).map(Self)
1061 }
1062
1063 pub fn checked_sub(self, offset: u32) -> Option<Self> {
1065 self.0.checked_sub(offset).map(Self)
1066 }
1067
1068 pub const fn saturating_add(self, offset: u32) -> Self {
1070 Self(self.0.saturating_add(offset))
1071 }
1072
1073 pub const fn saturating_add_signed(self, offset: i32) -> Self {
1076 Self(self.0.saturating_add_signed(offset))
1077 }
1078
1079 pub const fn saturating_sub(self, offset: u32) -> Self {
1081 Self(self.0.saturating_sub(offset))
1082 }
1083 }
1084
1085 impl From<u32> for $index_name {
1086 #[inline]
1087 fn from(index: u32) -> Self {
1088 Self(index)
1089 }
1090 }
1091
1092 impl From<$number_name> for $index_name {
1093 #[inline]
1094 fn from(index: $number_name) -> Self {
1095 Self(index.to_u32() - 1)
1096 }
1097 }
1098
1099 impl core::ops::Add<u32> for $index_name {
1100 type Output = Self;
1101
1102 #[inline]
1103 fn add(self, rhs: u32) -> Self {
1104 Self(self.0 + rhs)
1105 }
1106 }
1107
1108 impl core::ops::AddAssign<u32> for $index_name {
1109 fn add_assign(&mut self, rhs: u32) {
1110 let result = *self + rhs;
1111 *self = result;
1112 }
1113 }
1114
1115 impl core::ops::Add<i32> for $index_name {
1116 type Output = Self;
1117
1118 fn add(self, rhs: i32) -> Self {
1119 self.checked_add_signed(rhs).expect("invalid offset: overflow occurred")
1120 }
1121 }
1122
1123 impl core::ops::AddAssign<i32> for $index_name {
1124 fn add_assign(&mut self, rhs: i32) {
1125 let result = *self + rhs;
1126 *self = result;
1127 }
1128 }
1129
1130 impl core::ops::Sub<u32> for $index_name {
1131 type Output = Self;
1132
1133 #[inline]
1134 fn sub(self, rhs: u32) -> Self {
1135 Self(self.0 - rhs)
1136 }
1137 }
1138
1139 impl core::ops::SubAssign<u32> for $index_name {
1140 fn sub_assign(&mut self, rhs: u32) {
1141 let result = *self - rhs;
1142 *self = result;
1143 }
1144 }
1145
1146 impl fmt::Display for $index_name {
1147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1148 fmt::Display::fmt(&self.0, f)
1149 }
1150 }
1151
1152 #[cfg(feature = "arbitrary")]
1153 impl Arbitrary for $index_name {
1154 type Parameters = ();
1155 type Strategy = BoxedStrategy<Self>;
1156
1157 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1158 any::<u32>().prop_map(Self).boxed()
1159 }
1160 }
1161
1162 #[doc = concat!("A one-indexed ", $description, " number")]
1163 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1164 #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
1165 #[cfg_attr(feature = "serde", serde(transparent))]
1166 #[cfg_attr(
1167 all(feature = "arbitrary", test),
1168 miden_test_serialization_macros::serialization_test
1169 )]
1170 pub struct $number_name(NonZeroU32);
1171
1172 impl Default for $number_name {
1173 fn default() -> Self {
1174 Self(unsafe { NonZeroU32::new_unchecked(1) })
1175 }
1176 }
1177
1178 impl $number_name {
1179 pub const fn new(number: u32) -> Option<Self> {
1180 match NonZeroU32::new(number) {
1181 Some(num) => Some(Self(num)),
1182 None => None,
1183 }
1184 }
1185
1186 #[doc = concat!("Convert to a [", stringify!($index_name), "]")]
1187 pub const fn to_index(self) -> $index_name {
1188 $index_name(self.to_u32().saturating_sub(1))
1189 }
1190
1191 #[inline(always)]
1193 pub const fn to_usize(self) -> usize {
1194 self.0.get() as usize
1195 }
1196
1197 #[inline(always)]
1199 pub const fn to_u32(self) -> u32 {
1200 self.0.get()
1201 }
1202
1203 pub fn checked_add(self, offset: u32) -> Option<Self> {
1205 self.0.checked_add(offset).map(Self)
1206 }
1207
1208 pub fn checked_add_signed(self, offset: i32) -> Option<Self> {
1210 self.0.get().checked_add_signed(offset).and_then(Self::new)
1211 }
1212
1213 pub fn checked_sub(self, offset: u32) -> Option<Self> {
1215 self.0.get().checked_sub(offset).and_then(Self::new)
1216 }
1217
1218 pub const fn saturating_add(self, offset: u32) -> Self {
1220 Self(unsafe { NonZeroU32::new_unchecked(self.0.get().saturating_add(offset)) })
1221 }
1222
1223 pub fn saturating_add_signed(self, offset: i32) -> Self {
1226 Self::new(self.to_u32().saturating_add_signed(offset)).unwrap_or_default()
1227 }
1228
1229 pub fn saturating_sub(self, offset: u32) -> Self {
1231 Self::new(self.to_u32().saturating_sub(offset)).unwrap_or_default()
1232 }
1233 }
1234
1235 impl From<NonZeroU32> for $number_name {
1236 #[inline]
1237 fn from(index: NonZeroU32) -> Self {
1238 Self(index)
1239 }
1240 }
1241
1242 impl From<$index_name> for $number_name {
1243 #[inline]
1244 fn from(index: $index_name) -> Self {
1245 Self(unsafe { NonZeroU32::new_unchecked(index.to_u32() + 1) })
1246 }
1247 }
1248
1249 impl core::ops::Add<u32> for $number_name {
1250 type Output = Self;
1251
1252 #[inline]
1253 fn add(self, rhs: u32) -> Self {
1254 Self(unsafe { NonZeroU32::new_unchecked(self.0.get() + rhs) })
1255 }
1256 }
1257
1258 impl core::ops::AddAssign<u32> for $number_name {
1259 fn add_assign(&mut self, rhs: u32) {
1260 let result = *self + rhs;
1261 *self = result;
1262 }
1263 }
1264
1265 impl core::ops::Add<i32> for $number_name {
1266 type Output = Self;
1267
1268 fn add(self, rhs: i32) -> Self {
1269 self.to_u32()
1270 .checked_add_signed(rhs)
1271 .and_then(Self::new)
1272 .expect("invalid offset: overflow occurred")
1273 }
1274 }
1275
1276 impl core::ops::AddAssign<i32> for $number_name {
1277 fn add_assign(&mut self, rhs: i32) {
1278 let result = *self + rhs;
1279 *self = result;
1280 }
1281 }
1282
1283 impl core::ops::Sub<u32> for $number_name {
1284 type Output = Self;
1285
1286 #[inline]
1287 fn sub(self, rhs: u32) -> Self {
1288 self.to_u32()
1289 .checked_sub(rhs)
1290 .and_then(Self::new)
1291 .expect("invalid offset: overflow occurred")
1292 }
1293 }
1294
1295 impl core::ops::SubAssign<u32> for $number_name {
1296 fn sub_assign(&mut self, rhs: u32) {
1297 let result = *self - rhs;
1298 *self = result;
1299 }
1300 }
1301
1302 impl fmt::Display for $number_name {
1303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1304 fmt::Display::fmt(&self.0, f)
1305 }
1306 }
1307 };
1308}
1309
1310declare_dual_number_and_index_type!(Line, "line");
1311declare_dual_number_and_index_type!(Column, "column");
1312
1313impl Serializable for LineNumber {
1317 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1318 target.write_u32(self.to_u32());
1319 }
1320}
1321
1322impl Deserializable for LineNumber {
1323 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1324 let value = source.read_u32()?;
1325 Self::new(value)
1326 .ok_or_else(|| DeserializationError::InvalidValue("line number cannot be zero".into()))
1327 }
1328}
1329
1330impl Serializable for ColumnNumber {
1331 fn write_into<W: ByteWriter>(&self, target: &mut W) {
1332 target.write_u32(self.to_u32());
1333 }
1334}
1335
1336impl Deserializable for ColumnNumber {
1337 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1338 let value = source.read_u32()?;
1339 Self::new(value).ok_or_else(|| {
1340 DeserializationError::InvalidValue("column number cannot be zero".into())
1341 })
1342 }
1343}
1344
1345#[cfg(feature = "arbitrary")]
1346impl Arbitrary for LineNumber {
1347 type Parameters = ();
1348 type Strategy = BoxedStrategy<Self>;
1349
1350 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1351 (1..=u32::MAX)
1352 .prop_map(|value| Self::new(value).expect("non-zero value"))
1353 .boxed()
1354 }
1355}
1356
1357#[cfg(feature = "arbitrary")]
1358impl Arbitrary for ColumnNumber {
1359 type Parameters = ();
1360 type Strategy = BoxedStrategy<Self>;
1361
1362 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1363 (1..=u32::MAX)
1364 .prop_map(|value| Self::new(value).expect("non-zero value"))
1365 .boxed()
1366 }
1367}
1368
1369#[cfg(test)]
1370mod tests {
1371 use super::*;
1372
1373 #[test]
1374 fn source_content_line_starts() {
1375 const CONTENT: &str = "\
1376begin
1377 push.1
1378 push.2
1379 add
1380end
1381";
1382 let content = SourceContent::new("masm", "foo.masm", CONTENT);
1383
1384 assert_eq!(content.line_count(), 6);
1385 assert_eq!(
1386 content
1387 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1388 .expect("invalid byte range"),
1389 "begin\n".as_bytes()
1390 );
1391 assert_eq!(
1392 content
1393 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1394 .expect("invalid byte range"),
1395 " push.1\n".as_bytes()
1396 );
1397 assert_eq!(
1398 content
1399 .byte_slice(content.line_range(content.last_line_index()).expect("invalid line"))
1400 .expect("invalid byte range"),
1401 "".as_bytes()
1402 );
1403 }
1404
1405 #[test]
1406 fn source_content_line_starts_after_update() {
1407 const CONTENT: &str = "\
1408begin
1409 push.1
1410 push.2
1411 add
1412end
1413";
1414 const FRAGMENT: &str = " push.2
1415 mul
1416end
1417";
1418 let mut content = SourceContent::new("masm", "foo.masm", CONTENT);
1419 content
1420 .update(FRAGMENT.to_string(), Some(Selection::from(LineIndex(4)..LineIndex(5))), 1)
1421 .expect("update failed");
1422
1423 assert_eq!(
1424 content.as_str(),
1425 "\
1426begin
1427 push.1
1428 push.2
1429 add
1430 push.2
1431 mul
1432end
1433"
1434 );
1435 assert_eq!(content.line_count(), 8);
1436 assert_eq!(
1437 content
1438 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1439 .expect("invalid byte range"),
1440 "begin\n".as_bytes()
1441 );
1442 assert_eq!(
1443 content
1444 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1445 .expect("invalid byte range"),
1446 " add\n".as_bytes()
1447 );
1448 assert_eq!(
1449 content
1450 .byte_slice(content.line_range(LineIndex(4)).expect("invalid line"))
1451 .expect("invalid byte range"),
1452 " push.2\n".as_bytes()
1453 );
1454 assert_eq!(
1455 content
1456 .byte_slice(content.line_range(content.last_line_index()).expect("invalid line"))
1457 .expect("invalid byte range"),
1458 "".as_bytes()
1459 );
1460 }
1461
1462 #[test]
1464 fn source_content_line_starts_with_trailing_backslash() {
1465 const CONTENT: &str =
1466 "//! Build with:\n//! cargo build \\\n//! --release\nfn main() {}\n";
1467
1468 let content = SourceContent::new("rust", "example.rs", CONTENT);
1469
1470 assert_eq!(content.line_count(), 5);
1477
1478 assert_eq!(
1480 content
1481 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1482 .expect("invalid byte range"),
1483 "//! Build with:\n".as_bytes()
1484 );
1485 assert_eq!(
1486 content
1487 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1488 .expect("invalid byte range"),
1489 "//! cargo build \\\n".as_bytes()
1490 );
1491 assert_eq!(
1492 content
1493 .byte_slice(content.line_range(LineIndex(2)).expect("invalid line"))
1494 .expect("invalid byte range"),
1495 "//! --release\n".as_bytes()
1496 );
1497 assert_eq!(
1498 content
1499 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1500 .expect("invalid byte range"),
1501 "fn main() {}\n".as_bytes()
1502 );
1503
1504 let offset_line0 = content.line_column_to_offset(LineIndex(0), ColumnIndex(0));
1507 let offset_line1 = content.line_column_to_offset(LineIndex(1), ColumnIndex(0));
1508 let offset_line2 = content.line_column_to_offset(LineIndex(2), ColumnIndex(0));
1509 let offset_line3 = content.line_column_to_offset(LineIndex(3), ColumnIndex(0));
1510
1511 assert!(offset_line0.is_some(), "line 0 should be accessible");
1512 assert!(offset_line1.is_some(), "line 1 should be accessible");
1513 assert!(offset_line2.is_some(), "line 2 should be accessible");
1514 assert!(offset_line3.is_some(), "line 3 should be accessible");
1515
1516 assert_eq!(offset_line0.unwrap().to_u32(), 0);
1518 assert_eq!(offset_line1.unwrap().to_u32(), 16); assert_eq!(offset_line2.unwrap().to_u32(), 36); assert_eq!(offset_line3.unwrap().to_u32(), 54); }
1522
1523 #[test]
1525 fn source_content_line_starts_multiple_trailing_backslashes() {
1526 const CONTENT: &str = "line1 \\\nline2 \\\nline3 \\\nline4\n";
1528
1529 let content = SourceContent::new("text", "test.txt", CONTENT);
1530
1531 assert_eq!(content.line_count(), 5);
1533
1534 assert_eq!(
1536 content
1537 .byte_slice(content.line_range(LineIndex(0)).expect("invalid line"))
1538 .expect("invalid byte range"),
1539 "line1 \\\n".as_bytes()
1540 );
1541 assert_eq!(
1542 content
1543 .byte_slice(content.line_range(LineIndex(1)).expect("invalid line"))
1544 .expect("invalid byte range"),
1545 "line2 \\\n".as_bytes()
1546 );
1547 assert_eq!(
1548 content
1549 .byte_slice(content.line_range(LineIndex(2)).expect("invalid line"))
1550 .expect("invalid byte range"),
1551 "line3 \\\n".as_bytes()
1552 );
1553 assert_eq!(
1554 content
1555 .byte_slice(content.line_range(LineIndex(3)).expect("invalid line"))
1556 .expect("invalid byte range"),
1557 "line4\n".as_bytes()
1558 );
1559 }
1560
1561 #[test]
1562 fn source_content_line_column_to_offset_multibyte_utf8() {
1563 const CONTENT: &str = "héllo\n";
1565 let content = SourceContent::new("text", "test.txt", CONTENT);
1566
1567 let expected = [(0, 0u32), (1, 1), (2, 3), (3, 4), (4, 5), (5, 6)];
1568 for (column, expected_byte) in expected {
1569 let offset = content
1570 .line_column_to_offset(LineIndex(0), ColumnIndex(column))
1571 .unwrap_or_else(|| panic!("column {column} should be in bounds"));
1572 assert_eq!(offset.to_u32(), expected_byte, "wrong byte offset for column {column}");
1573 }
1574
1575 for (column, _) in expected {
1576 let offset = content.line_column_to_offset(LineIndex(0), ColumnIndex(column)).unwrap();
1577 let loc = content.location(offset).unwrap();
1578 assert_eq!(
1579 ColumnIndex::from(loc.column).to_u32(),
1580 column,
1581 "round-trip mismatch at column {column}"
1582 );
1583 }
1584 }
1585
1586 #[test]
1587 fn source_content_line_column_to_offset_rejects_line_terminator() {
1588 let lf = SourceContent::new("text", "test.txt", "ab\ncd");
1589 assert!(lf.line_column_to_offset(LineIndex(0), ColumnIndex(2)).is_some());
1590 assert!(lf.line_column_to_offset(LineIndex(0), ColumnIndex(3)).is_none());
1591
1592 let crlf = SourceContent::new("text", "test.txt", "ab\r\ncd");
1593 assert!(crlf.line_column_to_offset(LineIndex(0), ColumnIndex(2)).is_some());
1594 assert!(crlf.line_column_to_offset(LineIndex(0), ColumnIndex(3)).is_none());
1595 assert!(crlf.line_column_to_offset(LineIndex(0), ColumnIndex(4)).is_none());
1596 }
1597
1598 #[test]
1599 fn source_content_line_column_to_offset_astral_character() {
1600 const CONTENT: &str = "\u{1F600}x";
1602 let content = SourceContent::new("text", "test.txt", CONTENT);
1603
1604 assert_eq!(
1605 content.line_column_to_offset(LineIndex(0), ColumnIndex(0)).unwrap().to_u32(),
1606 0
1607 );
1608 assert_eq!(
1609 content.line_column_to_offset(LineIndex(0), ColumnIndex(1)).unwrap().to_u32(),
1610 4
1611 );
1612 assert_eq!(
1613 content.line_column_to_offset(LineIndex(0), ColumnIndex(2)).unwrap().to_u32(),
1614 5
1615 );
1616 }
1617
1618 #[test]
1619 fn source_content_update_rejects_same_line_selection_spanning_line_terminator() {
1620 let mut content = SourceContent::new("text", "test.txt", "a\nb");
1621 assert_eq!(content.line_count(), 2);
1622
1623 let selection = Selection::new(Position::new(0, 1), Position::new(0, 2));
1624 let result = content.update(String::new(), Some(selection), 1);
1625
1626 assert!(
1627 result.is_err(),
1628 "expected the same-line selection spanning the line terminator to be rejected, got: \
1629 {result:?}"
1630 );
1631 assert_eq!(content.line_count(), 2);
1632 }
1633}