use core::{ops, str};
#[macro_export]
macro_rules! position {
($haystack:expr, $needle:expr) => {{ const POSITION: ::core::ops::Range<usize> = $crate::position($haystack, $needle); POSITION }};
}
#[inline(always)]
pub const fn position(haystack: &str, needle: &str) -> ops::Range<usize> {
let start = search(haystack, needle);
if start < 0 {
let _ = haystack.as_bytes()[haystack.len()];
}
let start = start as usize;
start..start + needle.len()
}
const fn search(haystack: &str, needle: &str) -> isize {
if needle.len() == 0 {
return 0;
}
let haystack = haystack.as_bytes();
let needle = needle.as_bytes();
if needle.len() <= haystack.len() {
if needle.len() == 1 {
let needle = needle[0];
let mut offset = 0;
while offset <= haystack.len() {
if haystack[offset] == needle {
return offset as isize;
}
offset += 1;
}
}
else {
let mut jumps = [max(needle.len()); 256];
let tail = needle.len() - 1;
let mut i = 0;
while i < tail {
jumps[needle[i] as usize] = max(tail - i);
i += 1;
}
let sentinel = needle[tail];
let mut offset = 0;
while offset < haystack.len() - tail {
let chr = haystack[offset + tail];
if chr == sentinel && check(haystack, needle, offset) {
return offset as isize;
}
offset += jumps[chr as usize] as usize;
}
}
}
return -1;
}
#[inline(always)]
const fn check(haystack: &[u8], needle: &[u8], offset: usize) -> bool {
let mut i = 0;
while i < needle.len() {
if haystack[offset + i] != needle[i] {
return false;
}
i += 1;
}
return true;
}
#[inline(always)]
const fn max(a: usize) -> u8 {
if a > 255 { 255 } else { a as u8 }
}
#[test]
fn test_position() {
assert_eq!(position("ABCBC", "CBC"), 2..5);
assert_eq!(position("ABCBC", "ABCBC"), 0..5);
}
#[test]
#[should_panic]
fn test_position_needle_longer_than_haystack() {
let _ = position("haystack", "needleneedleneedle");
}