Skip to content

Commit

Permalink
Switch from itoa to itoap and add required DeferredWriter API
Browse files Browse the repository at this point in the history
  • Loading branch information
jix committed Dec 26, 2021
1 parent 0486834 commit 19be237
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 12 deletions.
1 change: 0 additions & 1 deletion flussab-cnf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ keywords = ["cnf", "dimacs-cnf", "parser", "writer"]
categories = ["parser-implementations", "encoding"]

[dependencies]
itoa = "0.4.7"
flussab = { version = "0.1.0", path = "../flussab" }
thiserror = "1.0.24"
num-traits = "0.2.14"
6 changes: 3 additions & 3 deletions flussab-cnf/src/cnf.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Parsing and writing of the DIMACS CNF file format.
use std::io::{self, BufReader, Read, Write};
use std::io::{BufReader, Read, Write};

use flussab::{text::LineReader, DeferredReader, DeferredWriter};
use flussab::{text::LineReader, write, DeferredReader, DeferredWriter};

use crate::{error::ParseError, token, Dimacs};

Expand Down Expand Up @@ -219,7 +219,7 @@ pub fn write_header(writer: &mut DeferredWriter, header: Header) {
/// Writes a clause.
pub fn write_clause<L: Dimacs>(writer: &mut DeferredWriter, clause_lits: &[L]) {
for lit in clause_lits {
let _ = itoa::write(&mut *writer, lit.dimacs());
write::text::ascii_digits(writer, lit.dimacs());
writer.write_all_defer_err(b" ");
}
writer.write_all_defer_err(b"0\n")
Expand Down
8 changes: 4 additions & 4 deletions flussab-cnf/src/gcnf.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Parsing and writing of the GCNF file format for group oriented CNF formulas.
use std::io::{self, BufReader, Read, Write};
use std::io::{BufReader, Read, Write};

use flussab::{text::LineReader, DeferredReader, DeferredWriter};
use flussab::{text::LineReader, write, DeferredReader, DeferredWriter};

use crate::{error::ParseError, token, Dimacs};

Expand Down Expand Up @@ -252,10 +252,10 @@ pub fn write_header(writer: &mut DeferredWriter, header: Header) {
/// Writes a clause belonging to a group.
pub fn write_clause<L: Dimacs>(writer: &mut DeferredWriter, group: usize, clause_lits: &[L]) {
writer.write_all_defer_err(b"{");
let _ = itoa::write(&mut *writer, group);
write::text::ascii_digits(writer, group);
writer.write_all_defer_err(b"} ");
for lit in clause_lits {
let _ = itoa::write(&mut *writer, lit.dimacs());
write::text::ascii_digits(writer, lit.dimacs());
writer.write_all_defer_err(b" ");
}
writer.write_all_defer_err(b"0\n");
Expand Down
8 changes: 4 additions & 4 deletions flussab-cnf/src/wcnf.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Parsing and writing of the WCNF file format for weighted CNF formulas.
//!
//! Also known as WDIMACS.
use std::io::{self, BufReader, Read, Write};
use std::io::{BufReader, Read, Write};

use flussab::{text::LineReader, DeferredReader, DeferredWriter};
use flussab::{text::LineReader, write, DeferredReader, DeferredWriter};

use crate::{error::ParseError, token, Dimacs};

Expand Down Expand Up @@ -243,10 +243,10 @@ pub fn write_header(writer: &mut DeferredWriter, header: Header) {

/// Writes a weighted clause.
pub fn write_clause<L: Dimacs>(writer: &mut DeferredWriter, weight: u64, clause_lits: &[L]) {
let _ = itoa::write(&mut *writer, weight);
write::text::ascii_digits(writer, weight);
for lit in clause_lits {
writer.write_all_defer_err(b" ");
let _ = itoa::write(&mut *writer, lit.dimacs());
write::text::ascii_digits(writer, lit.dimacs());
}
writer.write_all_defer_err(b" 0\n");
}
1 change: 1 addition & 0 deletions flussab/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ keywords = ["parser", "streaming", "recursive-descent", "text", "binary"]
categories = ["parsing"]

[dependencies]
itoap = "1.0.1"
num-traits = "0.2.14"
38 changes: 38 additions & 0 deletions flussab/src/deferred_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,44 @@ impl<'a> DeferredWriter<'a> {
}
}

/// Returns a pointer to the current write pointer within the internal buffer if sufficient
/// space is available.
///
/// If only fewer than `len` bytes are available, this returns a null pointer.
///
/// Can be used in conjunction with [`advance_unchecked`][Self::advance_unchecked] to construct
/// output data directly within the output buffer, potentially avoiding a redundant copy.
#[inline]
pub fn buf_write_ptr(&mut self, len: usize) -> *mut u8 {
let old_len = self.buf.len();
// SAFETY add cannot overflow as both are at most `isize::MAX`.
let new_len = old_len + len;
if new_len <= self.buf.capacity() {
// SAFETY this returns the offset to `old_len` which is always in range
unsafe { self.buf.as_mut_ptr().add(old_len) }
} else {
std::ptr::null_mut()
}
}

/// Advances the write pointer within the internal buffer.
///
/// # Safety
///
/// This assumes that a) there is sufficient space left in the buffer and b) that the bytes the
/// pointer is advanced over were initialized prior to calling this (via
/// [`buf_write_ptr`](Self::buf_write_ptr)).
///
/// If either assumption does not hold calling this results in undefined behavior.
#[inline]
pub unsafe fn advance_unchecked(&mut self, len: usize) {
let old_len = self.buf.len();
// SAFETY add cannot overflow as both are at most `isize::MAX`.
let new_len = old_len + len;
debug_assert!(new_len <= self.buf.capacity());
self.buf.set_len(new_len)
}

/// Returns an encountered IO errors as `Err(io_err)`.
///
/// This resets the stored IO error and returns `Ok(())` if no IO error is stored.
Expand Down
1 change: 1 addition & 0 deletions flussab/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ mod deferred_reader;
mod deferred_writer;
mod parser;
pub mod text;
pub mod write;

pub use deferred_reader::DeferredReader;
pub use deferred_writer::DeferredWriter;
Expand Down
2 changes: 2 additions & 0 deletions flussab/src/write.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//! Utilities for writing data using a [`DeferredWriter`][crate::DeferredWriter].
pub mod text;
63 changes: 63 additions & 0 deletions flussab/src/write/text.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! Utilities for emitting text based formats using a [`DeferredWriter`].
use crate::DeferredWriter;

mod sealed {
pub trait Sealed: itoap::Integer {}

impl Sealed for i8 {}
impl Sealed for u8 {}
impl Sealed for i16 {}
impl Sealed for u16 {}
impl Sealed for i32 {}
impl Sealed for u32 {}
impl Sealed for i64 {}
impl Sealed for u64 {}
impl Sealed for i128 {}
impl Sealed for u128 {}
impl Sealed for isize {}
impl Sealed for usize {}
}

/// Primitive integer types that can be efficiently written into a [`DeferredWriter`].
pub trait Integer: sealed::Sealed {}

impl Integer for i8 {}
impl Integer for u8 {}
impl Integer for i16 {}
impl Integer for u16 {}
impl Integer for i32 {}
impl Integer for u32 {}
impl Integer for i64 {}
impl Integer for u64 {}
impl Integer for i128 {}
impl Integer for u128 {}
impl Integer for isize {}
impl Integer for usize {}

/// Write a decimal number using ASCII digits.
#[inline]
pub fn ascii_digits<I>(writer: &mut DeferredWriter, value: I)
where
I: Integer,
{
let ptr = writer.buf_write_ptr(I::MAX_LEN);
if ptr.is_null() {
ascii_digits_cold(writer, value)
} else {
// SAFETY above we requested space for `I::MAX_LEN` which is the most `itoap::write_to_ptr`
// will write. It returns the number of bytes written, so we can safely advance by it.
unsafe {
let len = itoap::write_to_ptr(ptr, value);
writer.advance_unchecked(len);
}
}
}

#[inline(never)]
#[cold]
fn ascii_digits_cold<I>(writer: &mut DeferredWriter, value: I)
where
I: Integer,
{
let _ = itoap::write(writer, value);
}

0 comments on commit 19be237

Please sign in to comment.