memchr.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
  2. // file at the top-level directory of this distribution and at
  3. // http://rust-lang.org/COPYRIGHT.
  4. //
  5. // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
  6. // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
  7. // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
  8. // option. This file may not be copied, modified, or distributed
  9. // except according to those terms.
  10. //
  11. // Original implementation taken from rust-memchr
  12. // Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
  13. pub use self::fallback::{memchr,memrchr};
  14. #[allow(dead_code)]
  15. mod fallback {
  16. use core::cmp;
  17. use core::mem;
  18. const LO_U64: u64 = 0x0101010101010101;
  19. const HI_U64: u64 = 0x8080808080808080;
  20. // use truncation
  21. const LO_USIZE: usize = LO_U64 as usize;
  22. const HI_USIZE: usize = HI_U64 as usize;
  23. /// Return `true` if `x` contains any zero byte.
  24. ///
  25. /// From *Matters Computational*, J. Arndt
  26. ///
  27. /// "The idea is to subtract one from each of the bytes and then look for
  28. /// bytes where the borrow propagated all the way to the most significant
  29. /// bit."
  30. #[inline]
  31. fn contains_zero_byte(x: usize) -> bool {
  32. x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0
  33. }
  34. #[cfg(target_pointer_width = "32")]
  35. #[inline]
  36. fn repeat_byte(b: u8) -> usize {
  37. let mut rep = (b as usize) << 8 | b as usize;
  38. rep = rep << 16 | rep;
  39. rep
  40. }
  41. #[cfg(target_pointer_width = "64")]
  42. #[inline]
  43. fn repeat_byte(b: u8) -> usize {
  44. let mut rep = (b as usize) << 8 | b as usize;
  45. rep = rep << 16 | rep;
  46. rep = rep << 32 | rep;
  47. rep
  48. }
  49. /// Return the first index matching the byte `a` in `text`.
  50. pub fn memchr(x: u8, text: &[u8]) -> Option<usize> {
  51. // Scan for a single byte value by reading two `usize` words at a time.
  52. //
  53. // Split `text` in three parts
  54. // - unaligned initial part, before the first word aligned address in text
  55. // - body, scan by 2 words at a time
  56. // - the last remaining part, < 2 word size
  57. let len = text.len();
  58. let ptr = text.as_ptr();
  59. let usize_bytes = mem::size_of::<usize>();
  60. // search up to an aligned boundary
  61. let align = (ptr as usize) & (usize_bytes- 1);
  62. let mut offset;
  63. if align > 0 {
  64. offset = cmp::min(usize_bytes - align, len);
  65. if let Some(index) = text[..offset].iter().position(|elt| *elt == x) {
  66. return Some(index);
  67. }
  68. } else {
  69. offset = 0;
  70. }
  71. // search the body of the text
  72. let repeated_x = repeat_byte(x);
  73. if len >= 2 * usize_bytes {
  74. while offset <= len - 2 * usize_bytes {
  75. unsafe {
  76. let u = *(ptr.offset(offset as isize) as *const usize);
  77. let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize);
  78. // break if there is a matching byte
  79. let zu = contains_zero_byte(u ^ repeated_x);
  80. let zv = contains_zero_byte(v ^ repeated_x);
  81. if zu || zv {
  82. break;
  83. }
  84. }
  85. offset += usize_bytes * 2;
  86. }
  87. }
  88. // find the byte after the point the body loop stopped
  89. text[offset..].iter().position(|elt| *elt == x).map(|i| offset + i)
  90. }
  91. /// Return the last index matching the byte `a` in `text`.
  92. pub fn memrchr(x: u8, text: &[u8]) -> Option<usize> {
  93. // Scan for a single byte value by reading two `usize` words at a time.
  94. //
  95. // Split `text` in three parts
  96. // - unaligned tail, after the last word aligned address in text
  97. // - body, scan by 2 words at a time
  98. // - the first remaining bytes, < 2 word size
  99. let len = text.len();
  100. let ptr = text.as_ptr();
  101. let usize_bytes = mem::size_of::<usize>();
  102. // search to an aligned boundary
  103. let end_align = (ptr as usize + len) & (usize_bytes - 1);
  104. let mut offset;
  105. if end_align > 0 {
  106. offset = len - cmp::min(usize_bytes - end_align, len);
  107. if let Some(index) = text[offset..].iter().rposition(|elt| *elt == x) {
  108. return Some(offset + index);
  109. }
  110. } else {
  111. offset = len;
  112. }
  113. // search the body of the text
  114. let repeated_x = repeat_byte(x);
  115. while offset >= 2 * usize_bytes {
  116. unsafe {
  117. let u = *(ptr.offset(offset as isize - 2 * usize_bytes as isize) as *const usize);
  118. let v = *(ptr.offset(offset as isize - usize_bytes as isize) as *const usize);
  119. // break if there is a matching byte
  120. let zu = contains_zero_byte(u ^ repeated_x);
  121. let zv = contains_zero_byte(v ^ repeated_x);
  122. if zu || zv {
  123. break;
  124. }
  125. }
  126. offset -= 2 * usize_bytes;
  127. }
  128. // find the byte before the point the body loop stopped
  129. text[..offset].iter().rposition(|elt| *elt == x)
  130. }
  131. // test fallback implementations on all plattforms
  132. #[test]
  133. fn matches_one() {
  134. assert_eq!(Some(0), memchr(b'a', b"a"));
  135. }
  136. #[test]
  137. fn matches_begin() {
  138. assert_eq!(Some(0), memchr(b'a', b"aaaa"));
  139. }
  140. #[test]
  141. fn matches_end() {
  142. assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
  143. }
  144. #[test]
  145. fn matches_nul() {
  146. assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
  147. }
  148. #[test]
  149. fn matches_past_nul() {
  150. assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
  151. }
  152. #[test]
  153. fn no_match_empty() {
  154. assert_eq!(None, memchr(b'a', b""));
  155. }
  156. #[test]
  157. fn no_match() {
  158. assert_eq!(None, memchr(b'a', b"xyz"));
  159. }
  160. #[test]
  161. fn matches_one_reversed() {
  162. assert_eq!(Some(0), memrchr(b'a', b"a"));
  163. }
  164. #[test]
  165. fn matches_begin_reversed() {
  166. assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
  167. }
  168. #[test]
  169. fn matches_end_reversed() {
  170. assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
  171. }
  172. #[test]
  173. fn matches_nul_reversed() {
  174. assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
  175. }
  176. #[test]
  177. fn matches_past_nul_reversed() {
  178. assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
  179. }
  180. #[test]
  181. fn no_match_empty_reversed() {
  182. assert_eq!(None, memrchr(b'a', b""));
  183. }
  184. #[test]
  185. fn no_match_reversed() {
  186. assert_eq!(None, memrchr(b'a', b"xyz"));
  187. }
  188. }
  189. #[cfg(test)]
  190. mod tests {
  191. // test the implementations for the current plattform
  192. use super::{memchr, memrchr};
  193. #[test]
  194. fn matches_one() {
  195. assert_eq!(Some(0), memchr(b'a', b"a"));
  196. }
  197. #[test]
  198. fn matches_begin() {
  199. assert_eq!(Some(0), memchr(b'a', b"aaaa"));
  200. }
  201. #[test]
  202. fn matches_end() {
  203. assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
  204. }
  205. #[test]
  206. fn matches_nul() {
  207. assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
  208. }
  209. #[test]
  210. fn matches_past_nul() {
  211. assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
  212. }
  213. #[test]
  214. fn no_match_empty() {
  215. assert_eq!(None, memchr(b'a', b""));
  216. }
  217. #[test]
  218. fn no_match() {
  219. assert_eq!(None, memchr(b'a', b"xyz"));
  220. }
  221. #[test]
  222. fn matches_one_reversed() {
  223. assert_eq!(Some(0), memrchr(b'a', b"a"));
  224. }
  225. #[test]
  226. fn matches_begin_reversed() {
  227. assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
  228. }
  229. #[test]
  230. fn matches_end_reversed() {
  231. assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
  232. }
  233. #[test]
  234. fn matches_nul_reversed() {
  235. assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
  236. }
  237. #[test]
  238. fn matches_past_nul_reversed() {
  239. assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
  240. }
  241. #[test]
  242. fn no_match_empty_reversed() {
  243. assert_eq!(None, memrchr(b'a', b""));
  244. }
  245. #[test]
  246. fn no_match_reversed() {
  247. assert_eq!(None, memrchr(b'a', b"xyz"));
  248. }
  249. }