extern crate safemem;
pub fn lf() -> ByteLineEnding { ByteLineEnding::new(b'\n') }
pub fn crlf() -> TwoByteLineEnding { TwoByteLineEnding::new(b'\r', b'\n') }
pub trait LineEnding {
fn write_ending(&self, slice: &mut [u8]);
fn len(&self) -> usize;
}
pub struct ByteLineEnding {
byte: u8
}
impl ByteLineEnding {
pub fn new(byte: u8) -> ByteLineEnding {
ByteLineEnding {
byte
}
}
}
impl LineEnding for ByteLineEnding {
#[inline]
fn write_ending(&self, slice: &mut [u8]) {
slice[0] = self.byte;
}
#[inline]
fn len(&self) -> usize {
1
}
}
pub struct TwoByteLineEnding {
byte0: u8,
byte1: u8,
}
impl TwoByteLineEnding {
pub fn new(byte0: u8, byte1: u8) -> TwoByteLineEnding {
TwoByteLineEnding {
byte0,
byte1,
}
}
}
impl LineEnding for TwoByteLineEnding {
#[inline]
fn write_ending(&self, slice: &mut [u8]) {
slice[0] = self.byte0;
slice[1] = self.byte1;
}
#[inline]
fn len(&self) -> usize {
2
}
}
pub struct SliceLineEnding<'a> {
slice: &'a [u8]
}
impl<'a> SliceLineEnding<'a> {
pub fn new(slice: &[u8]) -> SliceLineEnding {
SliceLineEnding {
slice
}
}
}
impl<'a> LineEnding for SliceLineEnding<'a> {
#[inline]
fn write_ending(&self, slice: &mut [u8]) {
slice.copy_from_slice(self.slice);
}
#[inline]
fn len(&self) -> usize {
self.slice.len()
}
}
pub fn line_wrap<L: LineEnding>(
buf: &mut [u8],
input_len: usize,
line_len: usize,
line_ending: &L,
) -> usize {
assert!(line_ending.len() > 0);
if input_len <= line_len {
return 0;
}
let line_ending_len = line_ending.len();
let line_wrap_params = line_wrap_parameters(input_len, line_len, line_ending_len);
assert!(
buf.len() >= line_wrap_params.total_len,
"Buffer must be able to hold encoded data after line wrapping"
);
let last_line_start = input_len.checked_sub(line_wrap_params.last_line_len)
.expect("Last line start index underflow");
let new_line_start = line_wrap_params.total_full_wrapped_lines_len;
safemem::copy_over(
buf,
last_line_start,
new_line_start,
line_wrap_params.last_line_len,
);
let mut total_line_ending_bytes = 0;
let mut old_line_start = last_line_start;
let mut new_line_start = line_wrap_params.total_full_wrapped_lines_len;
for _ in 0..line_wrap_params.lines_with_endings {
let end_of_line_ending = new_line_start;
let start_of_line_ending = end_of_line_ending
.checked_sub(line_ending_len)
.expect("Line ending start index underflow");
old_line_start = old_line_start.checked_sub(line_len)
.expect("Old line start index underflow");
new_line_start = new_line_start.checked_sub(line_wrap_params.line_with_ending_len)
.expect("New line start index underflow");
safemem::copy_over(buf, old_line_start, new_line_start, line_len);
line_ending.write_ending(&mut buf[start_of_line_ending..(end_of_line_ending)]);
total_line_ending_bytes += line_ending_len;
}
assert_eq!(line_wrap_params.total_line_endings_len, total_line_ending_bytes);
total_line_ending_bytes
}
#[derive(Debug, PartialEq)]
struct LineWrapParameters {
line_with_ending_len: usize,
lines_with_endings: usize,
last_line_len: usize,
total_full_wrapped_lines_len: usize,
total_len: usize,
total_line_endings_len: usize,
}
#[inline]
fn line_wrap_parameters(
input_len: usize,
line_len: usize,
line_ending_len: usize,
) -> LineWrapParameters {
let line_with_ending_len = line_len
.checked_add(line_ending_len)
.expect("Line length with ending exceeds usize");
if input_len <= line_len {
return LineWrapParameters {
line_with_ending_len,
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 (lines_with_endings, last_line_len) = if input_len % line_len > 0 {
(input_len / line_len, input_len % line_len)
} else {
(input_len / line_len - 1, line_len)
};
let total_full_wrapped_lines_len = lines_with_endings
.checked_mul(line_with_ending_len)
.expect("Full lines with endings length exceeds usize");
let total_len = total_full_wrapped_lines_len
.checked_add(last_line_len)
.expect("All lines with endings length exceeds usize");
let total_line_endings_len = lines_with_endings
.checked_mul(line_ending_len)
.expect("Total line endings length exceeds usize");
LineWrapParameters {
line_with_ending_len,
lines_with_endings,
last_line_len,
total_full_wrapped_lines_len,
total_len,
total_line_endings_len,
}
}
#[cfg(test)]
mod tests;