Skip to content

Commit fd05aac

Browse files
committed
eytzinger binary search for span lines
1 parent f607a67 commit fd05aac

File tree

3 files changed

+92
-8
lines changed

3 files changed

+92
-8
lines changed

sway-types/src/eytzinger.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#[derive(Default)]
2+
pub struct Eytzinger<T>(Vec<T>, Vec<usize>);
3+
4+
#[derive(Debug)]
5+
pub struct Index {
6+
pub index: usize,
7+
pub original: usize,
8+
}
9+
10+
fn eytzinger<T: Copy>(a: &[T], b: &mut [T], mut i: usize, k: usize) -> usize {
11+
if k <= a.len() {
12+
i = eytzinger(a, b, i, 2 * k);
13+
b[k] = a[i];
14+
i += 1;
15+
i = eytzinger(a, b, i, 2 * k + 1);
16+
}
17+
i
18+
}
19+
20+
impl<T: Copy + Default> From<&[T]> for Eytzinger<T> {
21+
fn from(input: &[T]) -> Self {
22+
let mut result = vec![T::default(); input.len() + 1];
23+
eytzinger(&input[..], &mut result[..], 0, 1);
24+
let original = (0..input.len()).into_iter().collect::<Vec<_>>();
25+
let mut order = vec![input.len(); input.len() + 1];
26+
eytzinger(&original[..], &mut order[..], 0, 1);
27+
Self(result, order)
28+
}
29+
}
30+
31+
impl<T: Copy + Ord> Eytzinger<T> {
32+
pub fn get(&self, idx: usize) -> Option<&T> {
33+
self.0.get(idx)
34+
}
35+
36+
#[inline]
37+
pub fn binary_search(&self, target: T) -> Result<Index, Index> {
38+
let mut idx = 1;
39+
40+
while idx < self.0.len() {
41+
#[cfg(target_arch = "x86_64")]
42+
unsafe {
43+
use std::arch::x86_64::*;
44+
let prefetch = self.0.as_ptr().wrapping_offset(2 * idx as isize);
45+
_mm_prefetch::<_MM_HINT_T0>(std::ptr::addr_of!(prefetch) as *const i8);
46+
}
47+
let current = self.0[idx];
48+
idx = 2 * idx + usize::from(current < target);
49+
}
50+
51+
idx >>= idx.trailing_ones() + 1;
52+
53+
let r = Index {
54+
index: idx,
55+
original: self.1[idx],
56+
};
57+
58+
if self.0[idx] == target {
59+
Ok(r)
60+
} else {
61+
Err(r)
62+
}
63+
}
64+
}
65+
66+
#[test]
67+
fn ok_binary_search() {
68+
let v = Eytzinger::from(vec![1, 5, 10].as_slice());
69+
assert_eq!(v.binary_search(1).unwrap().original, 0);
70+
assert_eq!(v.binary_search(5).unwrap().original, 1);
71+
assert_eq!(v.binary_search(10).unwrap().original, 2);
72+
73+
assert_eq!(v.binary_search(0).unwrap_err().original, 0);
74+
assert_eq!(v.binary_search(2).unwrap_err().original, 1);
75+
76+
assert_eq!(v.binary_search(4).unwrap_err().original, 1);
77+
assert_eq!(v.binary_search(6).unwrap_err().original, 2);
78+
79+
assert_eq!(v.binary_search(9).unwrap_err().original, 2);
80+
assert_eq!(v.binary_search(11).unwrap_err().original, 3);
81+
}

sway-types/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub mod style;
2424

2525
pub mod ast;
2626

27+
mod eytzinger;
28+
2729
pub type Id = [u8; Bytes32::LEN];
2830
pub type Contract = [u8; ContractId::LEN];
2931

sway-types/src/span.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::eytzinger::{Eytzinger, Index};
12
use crate::SourceId;
23
use lazy_static::lazy_static;
34
use serde::{Deserialize, Serialize};
@@ -12,7 +13,7 @@ lazy_static! {
1213
static ref DUMMY_SPAN: Span = Span::new(
1314
Source {
1415
text: Arc::from(""),
15-
line_starts: Arc::new(vec![])
16+
line_starts: Arc::new(<_>::default())
1617
},
1718
0,
1819
0,
@@ -28,7 +29,7 @@ lazy_static! {
2829
pub struct Source {
2930
pub text: Arc<str>,
3031
#[serde(skip)]
31-
pub line_starts: Arc<Vec<usize>>,
32+
pub line_starts: Arc<Eytzinger<usize>>,
3233
}
3334

3435
impl serde::Serialize for Source {
@@ -49,15 +50,15 @@ impl<'de> serde::Deserialize<'de> for Source {
4950
}
5051

5152
impl Source {
52-
fn calc_line_starts(text: &str) -> Arc<Vec<usize>> {
53+
fn calc_line_starts(text: &str) -> Arc<Eytzinger<usize>> {
5354
let mut lines_starts = Vec::with_capacity(text.len() / 80);
5455
lines_starts.push(0);
5556
for (idx, c) in text.char_indices() {
5657
if c == '\n' {
5758
lines_starts.push(idx + c.len_utf8())
5859
}
5960
}
60-
Arc::new(lines_starts)
61+
Arc::new(lines_starts.as_slice().into())
6162
}
6263

6364
pub fn new(text: &str) -> Self {
@@ -72,10 +73,10 @@ impl Source {
7273
if position > self.text.len() || self.text.is_empty() {
7374
LineCol { line: 0, col: 0 }
7475
} else {
75-
let (line, line_start) = match self.line_starts.binary_search(&position) {
76-
Ok(line) => (line, self.line_starts.get(line)),
77-
Err(0) => (0, None),
78-
Err(line) => (line - 1, self.line_starts.get(line - 1)),
76+
let (line, line_start) = match self.line_starts.binary_search(position) {
77+
Ok(Index { original: line, .. }) => (line, self.line_starts.get(line)),
78+
Err(Index { original: 0, .. }) => (0, None),
79+
Err(Index { original: line, .. }) => (line - 1, self.line_starts.get(line - 1)),
7980
};
8081
line_start.map_or(LineCol { line: 0, col: 0 }, |line_start| LineCol {
8182
line,

0 commit comments

Comments
 (0)