diff --git a/src/include/pv.h b/src/include/pv.h index ccef7c3..a3e04d2 100644 --- a/src/include/pv.h +++ b/src/include/pv.h @@ -221,6 +221,7 @@ extern void pv_state_error_skip_block_set(pvstate_t, off_t); extern void pv_state_stop_at_size_set(pvstate_t, bool); extern void pv_state_sync_after_write_set(pvstate_t, bool); extern void pv_state_direct_io_set(pvstate_t, bool); +extern void pv_state_sparse_output_set(pvstate_t, bool); extern void pv_state_rate_limit_set(pvstate_t, off_t); extern void pv_state_target_buffer_size_set(pvstate_t, size_t); extern void pv_state_no_splice_set(pvstate_t, bool); diff --git a/src/main/main.c b/src/main/main.c index 742327c..7da13ca 100644 --- a/src/main/main.c +++ b/src/main/main.c @@ -509,6 +509,7 @@ int main(int argc, char **argv) pv_state_error_skip_block_set(state, opts->error_skip_block); pv_state_sync_after_write_set(state, opts->sync_after_write); pv_state_direct_io_set(state, opts->direct_io); + pv_state_sparse_output_set(state, opts->sparse_output); pv_state_discard_input_set(state, opts->discard_input); pv_state_rate_limit_set(state, opts->rate_limit); pv_state_target_buffer_size_set(state, opts->buffer_size); diff --git a/src/pv/state.c b/src/pv/state.c index 8163af7..4c106ca 100644 --- a/src/pv/state.c +++ b/src/pv/state.c @@ -131,6 +131,7 @@ void pv_reset_transfer(pvtransferstate_t transfer) transfer->line_positions_length = 0; transfer->line_positions_head = 0; transfer->last_output_position = 0; + transfer->output_not_seekable = false; } @@ -494,6 +495,11 @@ void pv_state_direct_io_set(pvstate_t state, bool val) state->control.direct_io_changed = true; } +void pv_state_sparse_output_set(pvstate_t state, bool val) +{ + state->control.sparse_output = val; +} + void pv_state_discard_input_set(pvstate_t state, bool val) { state->control.discard_input = val; diff --git a/src/pv/transfer.c b/src/pv/transfer.c index 1f65ca1..2d1a953 100644 --- a/src/pv/transfer.c +++ b/src/pv/transfer.c @@ -671,6 +671,62 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long nwritten = state->transfer.to_write; } else if (state->transfer.to_write > 0) { + /* + * In sparse output mode, check whether all of the bytes to + * be written are null, and if so, try to seek the output + * instead of writing the null bytes. + */ + if (state->control.sparse_output && !state->transfer.output_not_seekable) { + bool all_nulls = true; + size_t write_check_position, write_end_position; + write_check_position = state->transfer.write_position; + write_end_position = write_check_position + (size_t) (state->transfer.to_write); + while (all_nulls && write_check_position < write_end_position) { + if ('\0' == state->transfer.transfer_buffer[write_check_position]) { + write_check_position++; + } else { + all_nulls = false; + } + } + if (all_nulls) { + struct stat sb; + + /* + * Get the current size of the output file, + * so we can attempt to enlarge it with + * ftruncate() and then use lseek() to skip + * to the new end of the file. + * + * If any step fails, mark the output not + * seekable so we stop trying. + */ + memset(&sb, 0, sizeof(sb)); + + /*@+longintegral@ */ + /* + * splint has trouble with off_t / __off_t, in the lseek() call. + */ + if (0 != fstat(state->control.output_fd, &sb)) { + debug("%s: %s", "output fstat() failed", strerror(errno)); + state->transfer.output_not_seekable = true; + } else if (0 != + ftruncate(state->control.output_fd, + sb.st_size + (off_t) (state->transfer.to_write))) { + debug("%s: %s", "output ftruncate() failed", strerror(errno)); + state->transfer.output_not_seekable = true; + } else if (lseek(state->control.output_fd, (off_t) (state->transfer.to_write), SEEK_CUR) + == (off_t) - 1) { + debug("%s: %s", "output lseek() failed", strerror(errno)); + state->transfer.output_not_seekable = true; + } else { + /* Seek successful - skip write. */ + nwritten = state->transfer.to_write; + goto pv__transfer_write_completed; + } + /*@-longintegral@ */ + } + } + /* * Set an interval timer or an alarm to interrupt the write * with a signal if the write takes too long, so we can @@ -733,6 +789,9 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long debug("%s", "cancelling alarm"); (void) alarm(0); #endif /* HAVE_SETITIMER */ + + pv__transfer_write_completed: + /* If lseek() worked for sparse output, it jumps down here. */ } if (nwritten > 0) {