Skip to content

Commit 0b6da0b

Browse files
committed
add gzprintf
1 parent f4e9a0f commit 0b6da0b

File tree

8 files changed

+399
-114
lines changed

8 files changed

+399
-114
lines changed

.github/workflows/checks.yaml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ jobs:
345345
- name: Install rust toolchain
346346
uses: dtolnay/rust-toolchain@be73d7920c329f220ce78e0234b8f96b7ae60248
347347
with:
348-
toolchain: stable
348+
toolchain: nightly
349349
targets: ${{matrix.target}}
350350
- name: "cdylib: default settings"
351351
working-directory: libz-rs-sys-cdylib
@@ -387,12 +387,21 @@ jobs:
387387
cargo build --release --target ${{matrix.target}} --features=semver-prefix
388388
objdump -tT target/${{matrix.target}}/release/deps/libz_rs.so | grep -q -E "LIBZ_RS_SYS_v0.[0-9]+.x_uncompress" || (echo "symbol not found!" && exit 1)
389389
- run: sudo apt-get install -y valgrind
390+
- name: "cdylib: test_gzio"
391+
env:
392+
LD_LIBRARY_PATH: "target/${{matrix.target}}/release/deps"
393+
working-directory: libz-rs-sys-cdylib
394+
run: |
395+
cargo build --release --target ${{matrix.target}} --no-default-features --features=gz,gzprintf,c-allocator
396+
cc -DNO_STD -o test_gzio test_gzio.c target/${{matrix.target}}/release/deps/libz_rs.so
397+
./test_gzio
398+
valgrind --track-origins=yes --error-exitcode=1 ./test_gzio
390399
- name: "cdylib: example.c"
391400
env:
392401
LD_LIBRARY_PATH: "target/${{matrix.target}}/release/deps"
393402
working-directory: libz-rs-sys-cdylib
394403
run: |
395-
cargo build --release --target ${{matrix.target}} --no-default-features --features=gz,c-allocator
404+
cargo build --release --target ${{matrix.target}} --no-default-features --features=gz,gzprintf,c-allocator
396405
cc -DNO_STD -o example example.c target/${{matrix.target}}/release/deps/libz_rs.so
397406
./example
398407
valgrind --track-origins=yes --error-exitcode=1 ./example

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libz-rs-sys-cdylib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ custom-prefix = ["libz-rs-sys/custom-prefix"] # use the LIBZ_RS_SYS_PREFIX to pr
2828
semver-prefix = ["libz-rs-sys/semver-prefix"] # prefix all symbols in a semver-compatible way
2929
capi = []
3030
gz = ["dep:libc"] # support for the `gz*` functions is experimental
31+
gzprintf = []
3132
__internal-test = []
3233

3334
[dependencies]

libz-rs-sys-cdylib/example.c

Lines changed: 110 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -94,114 +94,114 @@ static void test_compress(unsigned char *compr, z_uintmax_t comprLen, unsigned c
9494
printf("uncompress(): %s\n", (char *)uncompr);
9595
}
9696

97-
// /* ===========================================================================
98-
// * Test read/write of .gz files
99-
// */
100-
// static void test_gzio(const char *fname, unsigned char *uncompr, z_size_t uncomprLen) {
101-
// #ifdef NO_GZCOMPRESS
102-
// fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
103-
// #else
104-
// int err;
105-
// size_t read;
106-
// size_t len = strlen(hello)+1;
107-
// gzFile file;
108-
// z_off64_t pos;
109-
// z_off64_t comprLen;
110-
//
111-
// /* Write gz file with test data */
112-
// file = PREFIX(gzopen)(fname, "wb");
113-
// if (file == NULL)
114-
// error("gzopen error\n");
115-
// /* Write hello, hello! using gzputs and gzprintf */
116-
// PREFIX(gzputc)(file, 'h');
117-
// if (PREFIX(gzputs)(file, "ello") != 4)
118-
// error("gzputs err: %s\n", PREFIX(gzerror)(file, &err));
119-
// if (PREFIX(gzprintf)(file, ", %s!", "hello") != 8)
120-
// error("gzprintf err: %s\n", PREFIX(gzerror)(file, &err));
121-
// /* Write string null-teriminator using gzseek */
122-
// if (PREFIX(gzseek)(file, 1L, SEEK_CUR) < 0)
123-
// error("gzseek error, gztell=%ld\n", (long)PREFIX(gztell)(file));
124-
// /* Write hello, hello! using gzfwrite using best compression level */
125-
// if (PREFIX(gzsetparams)(file, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY) != Z_OK)
126-
// error("gzsetparams err: %s\n", PREFIX(gzerror)(file, &err));
127-
// if (PREFIX(gzfwrite)(hello, len, 1, file) == 0)
128-
// error("gzfwrite err: %s\n", PREFIX(gzerror)(file, &err));
129-
// /* Flush compressed bytes to file */
130-
// if (PREFIX(gzflush)(file, Z_SYNC_FLUSH) != Z_OK)
131-
// error("gzflush err: %s\n", PREFIX(gzerror)(file, &err));
132-
// comprLen = PREFIX(gzoffset)(file);
133-
// if (comprLen <= 0)
134-
// error("gzoffset err: %s\n", PREFIX(gzerror)(file, &err));
135-
// PREFIX(gzclose)(file);
136-
//
137-
// /* Open gz file we previously wrote */
138-
// file = PREFIX(gzopen)(fname, "rb");
139-
// if (file == NULL)
140-
// error("gzopen error\n");
141-
//
142-
// /* Read uncompressed data - hello, hello! string twice */
143-
// strcpy((char*)uncompr, "garbages");
144-
// if (PREFIX(gzread)(file, uncompr, (unsigned)uncomprLen) != (int)(len + len))
145-
// error("gzread err: %s\n", PREFIX(gzerror)(file, &err));
146-
// if (strcmp((char*)uncompr, hello))
147-
// error("bad gzread: %s\n", (char*)uncompr);
148-
// else
149-
// printf("gzread(): %s\n", (char*)uncompr);
150-
// /* Check position at the end of the gz file */
151-
// if (PREFIX(gzeof)(file) != 1)
152-
// error("gzeof err: not reporting end of stream\n");
153-
//
154-
// /* Seek backwards mid-string and check char reading with gzgetc and gzungetc */
155-
// pos = PREFIX(gzseek)(file, -22L, SEEK_CUR);
156-
// if (pos != 6 || PREFIX(gztell)(file) != pos)
157-
// error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
158-
// if (PREFIX(gzgetc)(file) != ' ')
159-
// error("gzgetc error\n");
160-
// if (PREFIX(gzungetc)(' ', file) != ' ')
161-
// error("gzungetc error\n");
162-
// /* Read first hello, hello! string with gzgets */
163-
// strcpy((char*)uncompr, "garbages");
164-
// PREFIX(gzgets)(file, (char*)uncompr, (int)uncomprLen);
165-
// if (strlen((char*)uncompr) != 7) /* " hello!" */
166-
// error("gzgets err after gzseek: %s\n", PREFIX(gzerror)(file, &err));
167-
// if (strcmp((char*)uncompr, hello + 6))
168-
// error("bad gzgets after gzseek\n");
169-
// else
170-
// printf("gzgets() after gzseek: %s\n", (char*)uncompr);
171-
// /* Seek to second hello, hello! string */
172-
// pos = PREFIX(gzseek)(file, 14L, SEEK_SET);
173-
// if (pos != 14 || PREFIX(gztell)(file) != pos)
174-
// error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
175-
// /* Check position not at end of file */
176-
// if (PREFIX(gzeof)(file) != 0)
177-
// error("gzeof err: reporting end of stream\n");
178-
// /* Read first hello, hello! string with gzfread */
179-
// strcpy((char*)uncompr, "garbages");
180-
// read = PREFIX(gzfread)(uncompr, uncomprLen, 1, file);
181-
// if (strcmp((const char *)uncompr, hello) != 0)
182-
// error("bad gzgets\n");
183-
// else
184-
// printf("gzgets(): %s\n", (char*)uncompr);
185-
// pos = PREFIX(gzoffset)(file);
186-
// if (pos < 0 || pos != (comprLen + 10))
187-
// error("gzoffset err: wrong offset at end\n");
188-
// /* Trigger an error and clear it with gzclearerr */
189-
// PREFIX(gzfread)(uncompr, (size_t)-1, (size_t)-1, file);
190-
// PREFIX(gzerror)(file, &err);
191-
// if (err == 0)
192-
// error("gzerror err: no error returned\n");
193-
// PREFIX(gzclearerr)(file);
194-
// PREFIX(gzerror)(file, &err);
195-
// if (err != 0)
196-
// error("gzclearerr err: not zero %d\n", err);
197-
//
198-
// PREFIX(gzclose)(file);
199-
//
200-
// if (PREFIX(gzclose)(NULL) != Z_STREAM_ERROR)
201-
// error("gzclose unexpected return when handle null\n");
202-
// Z_UNUSED(read);
203-
// #endif
204-
// }
97+
/* ===========================================================================
98+
* Test read/write of .gz files
99+
*/
100+
static void test_gzio(const char *fname, unsigned char *uncompr, z_size_t uncomprLen) {
101+
#ifdef NO_GZCOMPRESS
102+
fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
103+
#else
104+
int err;
105+
size_t read;
106+
size_t len = strlen(hello)+1;
107+
gzFile file;
108+
z_off64_t pos;
109+
z_off64_t comprLen;
110+
111+
/* Write gz file with test data */
112+
file = PREFIX(gzopen)(fname, "wb");
113+
if (file == NULL)
114+
error("gzopen error\n");
115+
/* Write hello, hello! using gzputs and gzprintf */
116+
PREFIX(gzputc)(file, 'h');
117+
if (PREFIX(gzputs)(file, "ello") != 4)
118+
error("gzputs err: %s\n", PREFIX(gzerror)(file, &err));
119+
if (PREFIX(gzprintf)(file, ", %s!", "hello") != 8)
120+
error("gzprintf err: %s\n", PREFIX(gzerror)(file, &err));
121+
/* Write string null-teriminator using gzseek */
122+
if (PREFIX(gzseek)(file, 1L, SEEK_CUR) < 0)
123+
error("gzseek error, gztell=%ld\n", (long)PREFIX(gztell)(file));
124+
/* Write hello, hello! using gzfwrite using best compression level */
125+
if (PREFIX(gzsetparams)(file, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY) != Z_OK)
126+
error("gzsetparams err: %s\n", PREFIX(gzerror)(file, &err));
127+
if (PREFIX(gzfwrite)(hello, len, 1, file) == 0)
128+
error("gzfwrite err: %s\n", PREFIX(gzerror)(file, &err));
129+
/* Flush compressed bytes to file */
130+
if (PREFIX(gzflush)(file, Z_SYNC_FLUSH) != Z_OK)
131+
error("gzflush err: %s\n", PREFIX(gzerror)(file, &err));
132+
comprLen = PREFIX(gzoffset)(file);
133+
if (comprLen <= 0)
134+
error("gzoffset err: %s\n", PREFIX(gzerror)(file, &err));
135+
PREFIX(gzclose)(file);
136+
137+
/* Open gz file we previously wrote */
138+
file = PREFIX(gzopen)(fname, "rb");
139+
if (file == NULL)
140+
error("gzopen error\n");
141+
142+
/* Read uncompressed data - hello, hello! string twice */
143+
strcpy((char*)uncompr, "garbages");
144+
if (PREFIX(gzread)(file, uncompr, (unsigned)uncomprLen) != (int)(len + len))
145+
error("gzread err: %s\n", PREFIX(gzerror)(file, &err));
146+
if (strcmp((char*)uncompr, hello))
147+
error("bad gzread: %s\n", (char*)uncompr);
148+
else
149+
printf("gzread(): %s\n", (char*)uncompr);
150+
/* Check position at the end of the gz file */
151+
if (PREFIX(gzeof)(file) != 1)
152+
error("gzeof err: not reporting end of stream\n");
153+
154+
/* Seek backwards mid-string and check char reading with gzgetc and gzungetc */
155+
pos = PREFIX(gzseek)(file, -22L, SEEK_CUR);
156+
if (pos != 6 || PREFIX(gztell)(file) != pos)
157+
error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
158+
if (PREFIX(gzgetc)(file) != ' ')
159+
error("gzgetc error\n");
160+
if (PREFIX(gzungetc)(' ', file) != ' ')
161+
error("gzungetc error\n");
162+
/* Read first hello, hello! string with gzgets */
163+
strcpy((char*)uncompr, "garbages");
164+
PREFIX(gzgets)(file, (char*)uncompr, (int)uncomprLen);
165+
if (strlen((char*)uncompr) != 7) /* " hello!" */
166+
error("gzgets err after gzseek: %s\n", PREFIX(gzerror)(file, &err));
167+
if (strcmp((char*)uncompr, hello + 6))
168+
error("bad gzgets after gzseek\n");
169+
else
170+
printf("gzgets() after gzseek: %s\n", (char*)uncompr);
171+
/* Seek to second hello, hello! string */
172+
pos = PREFIX(gzseek)(file, 14L, SEEK_SET);
173+
if (pos != 14 || PREFIX(gztell)(file) != pos)
174+
error("gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)PREFIX(gztell)(file));
175+
/* Check position not at end of file */
176+
if (PREFIX(gzeof)(file) != 0)
177+
error("gzeof err: reporting end of stream\n");
178+
/* Read first hello, hello! string with gzfread */
179+
strcpy((char*)uncompr, "garbages");
180+
read = PREFIX(gzfread)(uncompr, uncomprLen, 1, file);
181+
if (strcmp((const char *)uncompr, hello) != 0)
182+
error("bad gzgets\n");
183+
else
184+
printf("gzgets(): %s\n", (char*)uncompr);
185+
pos = PREFIX(gzoffset)(file);
186+
if (pos < 0 || pos != (comprLen + 10))
187+
error("gzoffset err: wrong offset at end\n");
188+
/* Trigger an error and clear it with gzclearerr */
189+
PREFIX(gzfread)(uncompr, (size_t)-1, (size_t)-1, file);
190+
PREFIX(gzerror)(file, &err);
191+
if (err == 0)
192+
error("gzerror err: no error returned\n");
193+
PREFIX(gzclearerr)(file);
194+
PREFIX(gzerror)(file, &err);
195+
if (err != 0)
196+
error("gzclearerr err: not zero %d\n", err);
197+
198+
PREFIX(gzclose)(file);
199+
200+
if (PREFIX(gzclose)(NULL) != Z_STREAM_ERROR)
201+
error("gzclose unexpected return when handle null\n");
202+
Z_UNUSED(read);
203+
#endif
204+
}
205205

206206
/* ===========================================================================
207207
* Test deflate() with small buffers
@@ -977,8 +977,8 @@ int main(int argc, char *argv[]) {
977977

978978
test_compress(compr, comprLen, uncompr, uncomprLen);
979979

980-
// test_gzio((argc > 1 ? argv[1] : TESTFILE),
981-
// uncompr, uncomprLen);
980+
test_gzio((argc > 1 ? argv[1] : TESTFILE),
981+
uncompr, uncomprLen);
982982

983983
test_deflate(compr, comprLen);
984984
test_inflate(compr, comprLen, uncompr, uncomprLen);

libz-rs-sys-cdylib/src/gz.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2571,6 +2571,93 @@ pub unsafe extern "C-unwind" fn gzrewind(file: gzFile) -> c_int {
25712571
0
25722572
}
25732573

2574+
/// # Safety
2575+
///
2576+
/// The variadic arguments must correspond with the format string in number and type.
2577+
#[cfg(feature = "gzprintf")]
2578+
#[export_name = crate::prefix!(gzprintf)]
2579+
pub unsafe extern "C-unwind" fn gzprintf(
2580+
file: gzFile,
2581+
format: *const c_char,
2582+
mut va: ...
2583+
) -> c_int {
2584+
unsafe { gzvprintf(file, format, va.as_va_list()) }
2585+
}
2586+
2587+
#[cfg(feature = "gzprintf")]
2588+
unsafe extern "C-unwind" fn gzvprintf(
2589+
file: gzFile,
2590+
format: *const c_char,
2591+
va: core::ffi::VaList,
2592+
) -> c_int {
2593+
let Some(state) = (unsafe { file.cast::<GzState>().as_mut() }) else {
2594+
return Z_STREAM_ERROR;
2595+
};
2596+
2597+
// Check that we're writing and that there's no error.
2598+
if state.mode != GzMode::GZ_WRITE || state.err != Z_OK {
2599+
return Z_STREAM_ERROR;
2600+
}
2601+
2602+
// Make sure we have some buffer space.
2603+
if state.input.is_null() && gz_init(state).is_err() {
2604+
return state.err;
2605+
}
2606+
2607+
// Check for seek request.
2608+
if state.seek {
2609+
state.seek = false;
2610+
if gz_zero(state, state.skip as _).is_err() {
2611+
return state.err;
2612+
}
2613+
}
2614+
2615+
// Do the printf() into the input buffer, put length in len -- the input
2616+
// buffer is double-sized just for this function, so there is guaranteed to
2617+
// be state.size bytes available after the current contents
2618+
if state.stream.avail_in == 0 {
2619+
state.stream.next_in = state.input;
2620+
}
2621+
2622+
extern "C" {
2623+
fn vsnprintf(
2624+
s: *mut c_char,
2625+
n: libc::size_t,
2626+
format: *const c_char,
2627+
va: core::ffi::VaList,
2628+
) -> i32;
2629+
}
2630+
2631+
let next = state.input.wrapping_add(
2632+
(unsafe { state.stream.next_in.offset_from(state.input) })
2633+
.wrapping_add(state.stream.avail_in as isize) as usize,
2634+
);
2635+
let state_size = state.in_size / 2;
2636+
unsafe { *next.add(state_size - 1) = 0 };
2637+
let len = unsafe { vsnprintf(next.cast::<c_char>(), state_size, format, va) };
2638+
2639+
// Check that printf() results fit in buffer.
2640+
if len == 0 || len as usize >= state_size || unsafe { *next.add(state_size - 1) } != 0 {
2641+
return 0;
2642+
}
2643+
2644+
// Update buffer and position, compress first half if past that.
2645+
state.stream.avail_in += len as u32;
2646+
state.pos += i64::from(len);
2647+
if state.stream.avail_in as usize >= state_size {
2648+
let left = state.stream.avail_in - state_size as u32;
2649+
state.stream.avail_in = state_size as u32;
2650+
if gz_comp(state, Z_NO_FLUSH).is_err() {
2651+
return state.err;
2652+
}
2653+
unsafe { core::ptr::copy(state.input.add(state_size), state.input, left as usize) };
2654+
state.stream.next_in = state.input;
2655+
state.stream.avail_in = left;
2656+
}
2657+
2658+
len
2659+
}
2660+
25742661
// Create a deep copy of a C string using `ALLOCATOR`
25752662
//
25762663
// # Safety

libz-rs-sys-cdylib/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![cfg_attr(feature = "gzprintf", feature(c_variadic))]
12
extern crate libz_rs_sys;
23

34
pub use libz_rs_sys::*;

0 commit comments

Comments
 (0)