// The Computer Language Benchmarks Game // http://benchmarksgame.alioth.debian.org/ // // contributed by the Rust Project Developers // Copyright (c) 2013-2014 The Rust Project Developers // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // - Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in // the documentation and/or other materials provided with the // distribution. // // - Neither the name of "The Computer Language Benchmarks Game" nor // the name of "The Computer Language Shootout Benchmarks" nor the // names of its contributors may be used to endorse or promote // products derived from this software without specific prior // written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. // ignore-android see #10393 #13206 #![feature(unboxed_closures)] extern crate libc; use std::io::stdio::{stdin_raw, stdout_raw}; use std::io::{IoResult, EndOfFile}; use std::ptr::{copy_memory, Unique}; use std::thread::Thread; struct Tables { table8: [u8;1 << 8], table16: [u16;1 << 16] } impl Tables { fn new() -> Tables { let mut table8 = [0;1 << 8]; for (i, v) in table8.iter_mut().enumerate() { *v = Tables::computed_cpl8(i as u8); } let mut table16 = [0;1 << 16]; for (i, v) in table16.iter_mut().enumerate() { *v = (table8[i & 255] as u16) << 8 | table8[i >> 8] as u16; } Tables { table8: table8, table16: table16 } } fn computed_cpl8(c: u8) -> u8 { match c { b'A' | b'a' => b'T', b'C' | b'c' => b'G', b'G' | b'g' => b'C', b'T' | b't' => b'A', b'U' | b'u' => b'A', b'M' | b'm' => b'K', b'R' | b'r' => b'Y', b'W' | b'w' => b'W', b'S' | b's' => b'S', b'Y' | b'y' => b'R', b'K' | b'k' => b'M', b'V' | b'v' => b'B', b'H' | b'h' => b'D', b'D' | b'd' => b'H', b'B' | b'b' => b'V', b'N' | b'n' => b'N', i => i, } } /// Retreives the complement for `i`. fn cpl8(&self, i: u8) -> u8 { self.table8[i as uint] } /// Retreives the complement for `i`. fn cpl16(&self, i: u16) -> u16 { self.table16[i as uint] } } /// Reads all remaining bytes from the stream. fn read_to_end(r: &mut R) -> IoResult> { // As reading the input stream in memory is a bottleneck, we tune // Reader::read_to_end() with a fast growing policy to limit // recopies. If MREMAP_RETAIN is implemented in the linux kernel // and jemalloc use it, this trick will become useless. const CHUNK: uint = 64 * 1024; let mut vec = Vec::with_capacity(CHUNK); loop { // workaround: very fast growing let len = vec.len(); if vec.capacity() - len < CHUNK { let cap = vec.capacity(); let mult = if cap < 256 * 1024 * 1024 { 16 } else { 2 }; vec.reserve_exact(mult * cap - len); } match r.push_at_least(1, CHUNK, &mut vec) { Ok(_) => {} Err(ref e) if e.kind == EndOfFile => break, Err(e) => return Err(e) } } Ok(vec) } /// Finds the first position at which `b` occurs in `s`. fn memchr(h: &[u8], n: u8) -> Option { use libc::{c_void, c_int, size_t}; let res = unsafe { libc::memchr(h.as_ptr() as *const c_void, n as c_int, h.len() as size_t) }; if res.is_null() { None } else { Some(res as uint - h.as_ptr() as uint) } } /// A mutable iterator over DNA sequences struct MutDnaSeqs<'a> { s: &'a mut [u8] } fn mut_dna_seqs<'a>(s: &'a mut [u8]) -> MutDnaSeqs<'a> { MutDnaSeqs { s: s } } impl<'a> Iterator for MutDnaSeqs<'a> { type Item = &'a mut [u8]; fn next(&mut self) -> Option<&'a mut [u8]> { let tmp = std::mem::replace(&mut self.s, &mut []); let tmp = match memchr(tmp, b'\n') { Some(i) => tmp.slice_from_mut(i + 1), None => return None, }; let (seq, tmp) = match memchr(tmp, b'>') { Some(i) => tmp.split_at_mut(i), None => { let len = tmp.len(); tmp.split_at_mut(len) } }; self.s = tmp; Some(seq) } } /// Length of a normal line without the terminating \n. const LINE_LEN: uint = 60; /// Compute the reverse complement. fn reverse_complement(seq: &mut [u8], tables: &Tables) { let seq = seq.init_mut();// Drop the last newline let len = seq.len(); let off = LINE_LEN - len % (LINE_LEN + 1); let mut i = LINE_LEN; while i < len { unsafe { copy_memory(seq.as_mut_ptr().offset((i - off + 1) as int), seq.as_ptr().offset((i - off) as int), off); *seq.get_unchecked_mut(i - off) = b'\n'; } i += LINE_LEN + 1; } let div = len / 4; let rem = len % 4; unsafe { let mut left = seq.as_mut_ptr() as *mut u16; // This is slow if len % 2 != 0 but still faster than bytewise operations. let mut right = seq.as_mut_ptr().offset(len as int - 2) as *mut u16; let end = left.offset(div as int); while left != end { let tmp = tables.cpl16(*left); *left = tables.cpl16(*right); *right = tmp; left = left.offset(1); right = right.offset(-1); } let end = end as *mut u8; match rem { 1 => *end = tables.cpl8(*end), 2 => { let tmp = tables.cpl8(*end); *end = tables.cpl8(*end.offset(1)); *end.offset(1) = tmp; }, 3 => { *end.offset(1) = tables.cpl8(*end.offset(1)); let tmp = tables.cpl8(*end); *end = tables.cpl8(*end.offset(2)); *end.offset(2) = tmp; }, _ => { }, } } } struct Racy(T); unsafe impl Send for Racy {} /// Executes a closure in parallel over the given iterator over mutable slice. /// The closure `f` is run in parallel with an element of `iter`. fn parallel<'a, I, T, F>(iter: I, f: F) where T: 'a+Send + Sync, I: Iterator, F: Fn(&mut [T]) + Sync { use std::mem; use std::raw::Repr; iter.map(|chunk| { // Need to convert `f` and `chunk` to something that can cross the task // boundary. let f = Racy(&f as *const F as *const uint); let raw = Racy(chunk.repr()); Thread::scoped(move|| { let f = f.0 as *const F; unsafe { (*f)(mem::transmute(raw.0)) } }) }).collect::>(); } fn main() { let mut data = read_to_end(&mut stdin_raw()).unwrap(); let tables = &Tables::new(); parallel(mut_dna_seqs(data.as_mut_slice()), |&: seq| reverse_complement(seq, tables)); stdout_raw().write(data.as_mut_slice()).unwrap(); }