extern crate safemem;
use super::*;
#[derive(Debug, PartialEq)]
pub struct LineWrapParameters {
pub lines_with_endings: usize,
pub last_line_len: usize,
pub total_full_wrapped_lines_len: usize,
pub total_len: usize,
pub total_line_endings_len: usize,
}
pub fn line_wrap_parameters(
input_len: usize,
line_len: usize,
line_ending: LineEnding,
) -> LineWrapParameters {
let line_ending_len = line_ending.len();
if input_len <= line_len {
return LineWrapParameters {
lines_with_endings: 0,
last_line_len: input_len,
total_full_wrapped_lines_len: 0,
total_len: input_len,
total_line_endings_len: 0,
};
};
let (num_lines_with_endings, last_line_length) = if input_len % line_len > 0 {
(input_len / line_len, input_len % line_len)
} else {
(input_len / line_len - 1, line_len)
};
let single_full_line_with_ending_len = line_len
.checked_add(line_ending_len)
.expect("Line length with ending exceeds usize");
let total_full_wrapped_lines_len = num_lines_with_endings
.checked_mul(single_full_line_with_ending_len)
.expect("Full lines with endings length exceeds usize");
let total_all_wrapped_len = total_full_wrapped_lines_len
.checked_add(last_line_length)
.expect("All lines with endings length exceeds usize");
let total_line_endings_len = num_lines_with_endings
.checked_mul(line_ending_len)
.expect("Total line endings length exceeds usize");
LineWrapParameters {
lines_with_endings: num_lines_with_endings,
last_line_len: last_line_length,
total_full_wrapped_lines_len: total_full_wrapped_lines_len,
total_len: total_all_wrapped_len,
total_line_endings_len: total_line_endings_len,
}
}
pub fn line_wrap(
encoded_buf: &mut [u8],
input_len: usize,
line_len: usize,
line_ending: LineEnding,
) -> usize {
let line_wrap_params = line_wrap_parameters(input_len, line_len, line_ending);
assert!(
encoded_buf.len() >= line_wrap_params.total_len,
"Buffer must be able to hold encoded data after line wrapping"
);
let last_line_start = line_wrap_params
.lines_with_endings
.checked_mul(line_len)
.expect("Start of last line in input exceeds usize");
let new_line_start = line_wrap_params.total_full_wrapped_lines_len;
safemem::copy_over(
encoded_buf,
last_line_start,
new_line_start,
line_wrap_params.last_line_len,
);
let mut line_ending_bytes = 0;
let line_ending_len = line_ending.len();
for line_num in 0..line_wrap_params.lines_with_endings {
let lines_before_this_line = line_wrap_params.lines_with_endings - 1 - line_num;
let old_line_start = lines_before_this_line
.checked_mul(line_len)
.expect("Old line start index exceeds usize");
let new_line_start = lines_before_this_line
.checked_mul(line_ending_len)
.and_then(|i| i.checked_add(old_line_start))
.expect("New line start index exceeds usize");
safemem::copy_over(encoded_buf, old_line_start, new_line_start, line_len);
let after_new_line = new_line_start
.checked_add(line_len)
.expect("Line ending index exceeds usize");
match line_ending {
LineEnding::LF => {
encoded_buf[after_new_line] = b'\n';
line_ending_bytes += 1;
}
LineEnding::CRLF => {
encoded_buf[after_new_line] = b'\r';
encoded_buf[after_new_line
.checked_add(1)
.expect("Line ending index exceeds usize")] = b'\n';
line_ending_bytes += 2;
}
}
}
assert_eq!(line_wrap_params.total_line_endings_len, line_ending_bytes);
line_ending_bytes
}
#[cfg(test)]
mod tests {
extern crate rand;
use super::*;
use self::rand::distributions::{IndependentSample, Range};
use self::rand::Rng;
#[test]
fn line_params_perfect_multiple_of_line_length_lf() {
let params = line_wrap_parameters(100, 20, LineEnding::LF);
assert_eq!(
LineWrapParameters {
lines_with_endings: 4,
last_line_len: 20,
total_full_wrapped_lines_len: 84,
total_len: 104,
total_line_endings_len: 4,
},
params
);
}
#[test]
fn line_params_partial_last_line_crlf() {
let params = line_wrap_parameters(103, 20, LineEnding::CRLF);
assert_eq!(
LineWrapParameters {
lines_with_endings: 5,
last_line_len: 3,
total_full_wrapped_lines_len: 110,
total_len: 113,
total_line_endings_len: 10,
},
params
);
}
#[test]
fn line_params_line_len_1_crlf() {
let params = line_wrap_parameters(100, 1, LineEnding::CRLF);
assert_eq!(
LineWrapParameters {
lines_with_endings: 99,
last_line_len: 1,
total_full_wrapped_lines_len: 99 * 3,
total_len: 99 * 3 + 1,
total_line_endings_len: 99 * 2,
},
params
);
}
#[test]
fn line_params_line_len_longer_than_input_crlf() {
let params = line_wrap_parameters(100, 200, LineEnding::CRLF);
assert_eq!(
LineWrapParameters {
lines_with_endings: 0,
last_line_len: 100,
total_full_wrapped_lines_len: 0,
total_len: 100,
total_line_endings_len: 0,
},
params
);
}
#[test]
fn line_params_line_len_same_as_input_crlf() {
let params = line_wrap_parameters(100, 100, LineEnding::CRLF);
assert_eq!(
LineWrapParameters {
lines_with_endings: 0,
last_line_len: 100,
total_full_wrapped_lines_len: 0,
total_len: 100,
total_line_endings_len: 0,
},
params
);
}
#[test]
fn line_wrap_length_1_lf() {
let mut buf = vec![0x1, 0x2, 0x3, 0x4];
assert_eq!(3, do_line_wrap(&mut buf, 1, LineEnding::LF));
assert_eq!(vec![0x1, 0xA, 0x2, 0xA, 0x3, 0xA, 0x4], buf);
}
#[test]
fn line_wrap_length_1_crlf() {
let mut buf = vec![0x1, 0x2, 0x3, 0x4];
assert_eq!(6, do_line_wrap(&mut buf, 1, LineEnding::CRLF));
assert_eq!(vec![0x1, 0xD, 0xA, 0x2, 0xD, 0xA, 0x3, 0xD, 0xA, 0x4], buf);
}
#[test]
fn line_wrap_length_2_lf_full_lines() {
let mut buf = vec![0x1, 0x2, 0x3, 0x4];
assert_eq!(1, do_line_wrap(&mut buf, 2, LineEnding::LF));
assert_eq!(vec![0x1, 0x2, 0xA, 0x3, 0x4], buf);
}
#[test]
fn line_wrap_length_2_crlf_full_lines() {
let mut buf = vec![0x1, 0x2, 0x3, 0x4];
assert_eq!(2, do_line_wrap(&mut buf, 2, LineEnding::CRLF));
assert_eq!(vec![0x1, 0x2, 0xD, 0xA, 0x3, 0x4], buf);
}
#[test]
fn line_wrap_length_2_lf_partial_line() {
let mut buf = vec![0x1, 0x2, 0x3, 0x4, 0x5];
assert_eq!(2, do_line_wrap(&mut buf, 2, LineEnding::LF));
assert_eq!(vec![0x1, 0x2, 0xA, 0x3, 0x4, 0xA, 0x5], buf);
}
#[test]
fn line_wrap_length_2_crlf_partial_line() {
let mut buf = vec![0x1, 0x2, 0x3, 0x4, 0x5];
assert_eq!(4, do_line_wrap(&mut buf, 2, LineEnding::CRLF));
assert_eq!(vec![0x1, 0x2, 0xD, 0xA, 0x3, 0x4, 0xD, 0xA, 0x5], buf);
}
#[test]
fn line_wrap_random() {
let mut buf: Vec<u8> = Vec::new();
let buf_range = Range::new(10, 1000);
let line_range = Range::new(10, 100);
let mut rng = rand::weak_rng();
for _ in 0..10_000 {
buf.clear();
let buf_len = buf_range.ind_sample(&mut rng);
let line_len = line_range.ind_sample(&mut rng);
let line_ending = if rng.gen() {
LineEnding::LF
} else {
LineEnding::CRLF
};
let line_ending_len = line_ending.len();
for _ in 0..buf_len {
buf.push(rng.gen());
}
let line_wrap_params = line_wrap_parameters(buf_len, line_len, line_ending);
let not_wrapped_buf = buf.to_vec();
let _ = do_line_wrap(&mut buf, line_len, line_ending);
for line_ending_num in 0..line_wrap_params.lines_with_endings {
let line_ending_offset = (line_ending_num + 1) * line_len;
for _ in 0..line_ending_len {
let _ = buf.remove(line_ending_offset);
}
}
assert_eq!(not_wrapped_buf, buf);
}
}
fn do_line_wrap(buf: &mut Vec<u8>, line_len: usize, line_ending: LineEnding) -> usize {
let mut rng = rand::weak_rng();
let orig_len = buf.len();
for _ in 0..(1000 + 2 * orig_len) {
buf.push(rng.gen());
}
let mut before_line_wrap = buf.to_vec();
let params = line_wrap_parameters(orig_len, line_len, line_ending);
let bytes_written = line_wrap(&mut buf[..], orig_len, line_len, line_ending);
assert_eq!(params.total_line_endings_len, bytes_written);
assert_eq!(params.lines_with_endings * line_ending.len(), bytes_written);
assert_eq!(params.total_len, orig_len + bytes_written);
let start_of_untouched_data = orig_len + bytes_written;
assert_eq!(
before_line_wrap[start_of_untouched_data..],
buf[start_of_untouched_data..]
);
let bytes_written_precise_fit = line_wrap(
&mut before_line_wrap[0..(params.total_len)],
orig_len,
line_len,
line_ending,
);
assert_eq!(bytes_written, bytes_written_precise_fit);
assert_eq!(
&buf[0..(params.total_len)],
&before_line_wrap[0..(params.total_len)]
);
buf.truncate(orig_len + bytes_written);
bytes_written
}
}