Skip to content

Commit

Permalink
Rewrite position implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
CasualX committed Mar 27, 2021
1 parent e1eaef3 commit 6e5fbfe
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "obfstr"
version = "0.2.5"
version = "0.2.6"
edition = "2018"
license = "MIT"

Expand Down
57 changes: 5 additions & 52 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
Compiletime string constant obfuscation.
*/

#![no_std]
#![cfg_attr(not(test), no_std)]

use core::{char, fmt, ops, ptr, str};
use core::{char, fmt, ptr, str};

#[doc(hidden)]
pub mod wide;
Expand All @@ -15,6 +15,9 @@ pub mod cfo;
mod murmur3;
pub use self::murmur3::murmur3;

mod pos;
pub use self::pos::position;

//----------------------------------------------------------------

/// Compiletime random number generator.
Expand Down Expand Up @@ -115,56 +118,6 @@ pub const SEED: u64 = splitmix(hash(env!("OBFSTR_SEED")) as u64);

//----------------------------------------------------------------

/// Finds the position of the needle in the haystack at compiletime.
///
/// Produces a const-eval error if the needle is not a substring of the haystack.
///
/// ```
/// assert_eq!(obfstr::position!("haystack", "st"), 3..5);
///# assert_eq!(obfstr::position!("haystack", "haystack"), 0..8);
///# assert_eq!(obfstr::position!("haystack", "ck"), 6..8);
/// ```
#[macro_export]
macro_rules! position {
($haystack:expr, $needle:expr) => {{ const _POSITION_RANGE: ::core::ops::Range<usize> = $crate::position($haystack, $needle); _POSITION_RANGE }};
}

/// Finds the position of the needle in the haystack at compiletime.
///
/// Produces a const-eval error if the needle is not a substring of the haystack.
///
/// ```
/// const POSITION: std::ops::Range<usize> = obfstr::position("haystack", "st");
/// assert_eq!(POSITION, 3..5);
/// ```
#[inline(always)]
pub const fn position(haystack: &str, needle: &str) -> ops::Range<usize> {
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;
}
let mut offset = 0;
let haystack = haystack.as_bytes();
let needle = needle.as_bytes();
while offset + needle.len() <= haystack.len() {
if check(haystack, needle, offset) {
return offset..offset + needle.len();
}
offset += 1;
}
// Compile error if substring not found
let _ = haystack[haystack.len()];
return 0..0;
}

//----------------------------------------------------------------

/// Obfuscated string constant data.
///
/// This type represents the data baked in the binary and holds the key and obfuscated string.
Expand Down
129 changes: 129 additions & 0 deletions src/pos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use core::{ops, str};

/// Finds the position of the needle in the haystack at compiletime.
///
/// Produces a const-eval error if the needle is not a substring of the haystack.
///
/// # Examples
///
/// ```
/// assert_eq!(obfstr::position!("haystack", "st"), 3..5);
///# assert_eq!(obfstr::position!("haystack", "haystack"), 0..8);
///# assert_eq!(obfstr::position!("haystack", "ck"), 6..8);
/// ```
///
/// Use this API when pooling strings in a single obfstr:
///
/// ```
/// const POOL: &str = concat!("Foo", "Bar", "Baz");
///
/// obfstr::obfstr! { let pool = POOL; }
///
/// // Later, read strings from the pool
/// let foo = &pool[obfstr::position!(POOL, "Foo")];
/// let bar = &pool[obfstr::position!(POOL, "Bar")];
/// let baz = &pool[obfstr::position!(POOL, "Baz")];
/// ```
#[macro_export]
macro_rules! position {
($haystack:expr, $needle:expr) => {{ const POSITION: ::core::ops::Range<usize> = $crate::position($haystack, $needle); POSITION }};
}

/// Finds the position of the needle in the haystack at compiletime.
///
/// Produces a const-eval error if the needle is not a substring of the haystack.
///
/// ```
/// const POSITION: std::ops::Range<usize> = obfstr::position("haystack", "st");
/// assert_eq!(POSITION, 3..5);
/// ```
#[inline(always)]
pub const fn position(haystack: &str, needle: &str) -> ops::Range<usize> {
let start = search(haystack, needle);
// Panic if substring not found
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 {
// Short-circuit empty needles
if needle.len() == 0 {
return 0;
}

let haystack = haystack.as_bytes();
let needle = needle.as_bytes();

// Avoid overflow checks later
if needle.len() <= haystack.len() {
// Special case for needle length of 1
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;
}
}
// Full blown quicksearch
else {
// assumed:
// needle.len() >= 2
// needle.len() <= haystack.len()

// Initialize the jump table
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;
}
// Find the needle
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");
}

0 comments on commit 6e5fbfe

Please sign in to comment.