Skip to content

Commit f17702f

Browse files
committed
Merge remote-tracking branch 'origin' into cached-source
2 parents 80881bf + 261f6b9 commit f17702f

File tree

4 files changed

+101
-18
lines changed

4 files changed

+101
-18
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ ouroboros = "0.18.4"
4141

4242
codspeed-criterion-compat = { version = "2.7.2", default-features = false, optional = true }
4343
static_assertions = "1.1.0"
44-
simd-json = "=0.14.3"
44+
simd-json = "0.14.3"
4545

4646
[dev-dependencies]
4747
twox-hash = "2.1.0"

src/helpers.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,10 @@ pub trait SourceText<'a>: Default + Clone + ToString {
12601260
/// Returns a slice of the text specified by the byte range.
12611261
fn byte_slice(&self, range: Range<usize>) -> Self;
12621262

1263+
/// Returns a slice of the text specified by the byte range without bounds checking.
1264+
#[allow(unsafe_code)]
1265+
unsafe fn byte_slice_unchecked(&self, range: Range<usize>) -> Self;
1266+
12631267
/// Returns true if the text is empty.
12641268
fn is_empty(&self) -> bool;
12651269

@@ -1293,6 +1297,11 @@ impl<'a> SourceText<'a> for Rope<'a> {
12931297
self.byte_slice(range)
12941298
}
12951299

1300+
#[allow(unsafe_code)]
1301+
unsafe fn byte_slice_unchecked(&self, range: Range<usize>) -> Self {
1302+
self.byte_slice_unchecked(range)
1303+
}
1304+
12961305
#[inline]
12971306
fn is_empty(&self) -> bool {
12981307
self.is_empty()
@@ -1330,6 +1339,11 @@ impl<'a> SourceText<'a> for &'a str {
13301339
self.get(range).unwrap_or_default()
13311340
}
13321341

1342+
#[allow(unsafe_code)]
1343+
unsafe fn byte_slice_unchecked(&self, range: Range<usize>) -> Self {
1344+
self.get_unchecked(range)
1345+
}
1346+
13331347
#[inline]
13341348
fn is_empty(&self) -> bool {
13351349
(*self).is_empty()

src/rope.rs

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
use std::{
44
borrow::Cow,
5-
cell::RefCell,
65
collections::VecDeque,
76
hash::Hash,
87
ops::{Bound, RangeBounds},
@@ -733,6 +732,18 @@ impl PartialEq<Rope<'_>> for Rope<'_> {
733732
return false;
734733
}
735734

735+
if let (
736+
Rope {
737+
repr: Repr::Light(s),
738+
},
739+
Rope {
740+
repr: Repr::Light(other),
741+
},
742+
) = (self, other)
743+
{
744+
return s == other;
745+
}
746+
736747
let chunks = match &self.repr {
737748
Repr::Light(s) => &[(*s, 0)][..],
738749
Repr::Full(data) => &data[..],
@@ -742,22 +753,63 @@ impl PartialEq<Rope<'_>> for Rope<'_> {
742753
Repr::Full(data) => &data[..],
743754
};
744755

745-
let mut cur = 0;
746-
let other_chunk_index = RefCell::new(0);
747-
let mut other_chunk_byte_index = 0;
748-
let other_chunk = || other_chunks[*other_chunk_index.borrow()].0.as_bytes();
749-
for (chunk, start_pos) in chunks.iter() {
750-
let chunk = chunk.as_bytes();
751-
while (cur - start_pos) < chunk.len() {
752-
if other_chunk_byte_index >= other_chunk().len() {
753-
other_chunk_byte_index = 0;
754-
*other_chunk_index.borrow_mut() += 1;
756+
let total_bytes = self.len();
757+
let mut byte_idx = 0;
758+
759+
let mut chunks_idx = 0;
760+
let mut in_chunk_byte_idx = 0;
761+
762+
let mut other_chunks_idx = 0;
763+
let mut in_other_chunk_byte_idx = 0;
764+
765+
loop {
766+
if byte_idx == total_bytes {
767+
break;
768+
}
769+
770+
let &(chunk, _) = &chunks[chunks_idx];
771+
let chunk_len = chunk.len();
772+
let &(other_chunk, _) = &other_chunks[other_chunks_idx];
773+
let other_chunk_len = other_chunk.len();
774+
775+
let chunk_remaining = chunk_len - in_chunk_byte_idx;
776+
let other_chunk_remaining = other_chunk_len - in_other_chunk_byte_idx;
777+
778+
match chunk_remaining.cmp(&other_chunk_remaining) {
779+
std::cmp::Ordering::Less => {
780+
if other_chunk
781+
[in_other_chunk_byte_idx..in_other_chunk_byte_idx + chunk_remaining]
782+
!= chunk[in_chunk_byte_idx..]
783+
{
784+
return false;
785+
}
786+
in_other_chunk_byte_idx += chunk_remaining;
787+
chunks_idx += 1;
788+
in_chunk_byte_idx = 0;
789+
byte_idx += chunk_remaining;
755790
}
756-
if chunk[cur - start_pos] == other_chunk()[other_chunk_byte_index] {
757-
cur += 1;
758-
other_chunk_byte_index += 1;
759-
} else {
760-
return false;
791+
std::cmp::Ordering::Equal => {
792+
if chunk[in_chunk_byte_idx..]
793+
!= other_chunk[in_other_chunk_byte_idx..]
794+
{
795+
return false;
796+
}
797+
chunks_idx += 1;
798+
other_chunks_idx += 1;
799+
in_chunk_byte_idx = 0;
800+
in_other_chunk_byte_idx = 0;
801+
byte_idx += chunk_remaining;
802+
}
803+
std::cmp::Ordering::Greater => {
804+
if chunk[in_chunk_byte_idx..in_chunk_byte_idx + other_chunk_remaining]
805+
!= other_chunk[in_other_chunk_byte_idx..]
806+
{
807+
return false;
808+
}
809+
in_chunk_byte_idx += other_chunk_remaining;
810+
other_chunks_idx += 1;
811+
in_other_chunk_byte_idx = 0;
812+
byte_idx += other_chunk_remaining;
761813
}
762814
}
763815
}
@@ -1058,6 +1110,16 @@ mod tests {
10581110
b.add("fghi");
10591111

10601112
assert_eq!(a, b);
1113+
1114+
let mut a = Rope::new();
1115+
a.add("abc");
1116+
1117+
let mut b = Rope::new();
1118+
b.add("a");
1119+
b.add("b");
1120+
b.add("c");
1121+
1122+
assert_eq!(a, b);
10611123
}
10621124

10631125
#[test]

src/with_indices.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@ where
3939
let str_len = self.line.len();
4040
let start = *indices_indexes.get(start_index).unwrap_or(&str_len);
4141
let end = *indices_indexes.get(end_index).unwrap_or(&str_len);
42-
self.line.byte_slice(start..end)
42+
43+
#[allow(unsafe_code)]
44+
unsafe {
45+
// SAFETY: Since `indices` iterates over the `CharIndices` of `self`, we can guarantee
46+
// that the indices obtained from it will always be within the bounds of `self` and they
47+
// will always lie on UTF-8 sequence boundaries.
48+
self.line.byte_slice_unchecked(start..end)
49+
}
4350
}
4451
}
4552

0 commit comments

Comments
 (0)