From e9a301c42b156d70d00a30580c1a549f0f074213 Mon Sep 17 00:00:00 2001 From: Casper Date: Sun, 31 Jan 2021 15:54:30 +0100 Subject: [PATCH] Position API (#31) The position API is a compiletime function finding the position of a substring (needle) in a larger string (haystack). Its use case is pooling string literals together in a single obfstr instance where the original string can be sliced given its position in the larger pool. --- Cargo.toml | 2 +- src/lib.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b71c081..af07647 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "obfstr" -version = "0.2.2" +version = "0.2.3" edition = "2018" license = "MIT" diff --git a/src/lib.rs b/src/lib.rs index 8c72a36..3ba1f67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ Compiletime string constant obfuscation. #![feature(min_const_generics)] #![no_std] -use core::{char, fmt, str}; +use core::{char, fmt, ops, str}; //---------------------------------------------------------------- @@ -107,6 +107,58 @@ 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: ::core::ops::Range = $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 = obfstr::position("haystack", "st"); +/// assert_eq!(POSITION, 3..5); +/// ``` +pub const fn position(haystack: &str, needle: &str) -> ops::Range { + 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; +} + +//---------------------------------------------------------------- + /// Wide string constant, returns an array of words. /// /// The type of the returned constant is `&'static [u16; LEN]`.