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