Skip to content

Commit d0a6bf1

Browse files
committed
add a separate cdylib crate
1 parent c6d28e2 commit d0a6bf1

File tree

8 files changed

+543
-20
lines changed

8 files changed

+543
-20
lines changed

.github/workflows/checks.yaml

+30-16
Original file line numberDiff line numberDiff line change
@@ -257,20 +257,34 @@ jobs:
257257
with:
258258
toolchain: stable
259259
targets: ${{matrix.target}}
260-
- name: Rust cache
261-
uses: Swatinem/rust-cache@3cf7f8cc28d1b4e7d01e3783be10a97d55d483c8
262-
with:
263-
shared-key: "stable-${{matrix.target}}"
264-
- name: get zpipe.c
265-
run: wget https://www.zlib.net/zpipe.c
266-
- name: cargo build
267-
run: cargo build --target ${{matrix.target}} -p libz-rs-sys --release
268-
- name: cc
269-
run: cc -o zpipe zpipe.c target/${{matrix.target}}/release/deps/liblibz_rs_sys.so
270-
- name: execute
271-
run: cat Cargo.toml | ./zpipe | ./zpipe -d > out.txt
272-
- name: compare
273-
run: cmp -s Cargo.toml out.txt
260+
- name: "cdylib: default settings"
261+
working-directory: libz-rs-sys-cdylib
262+
run: |
263+
cargo build --release --target ${{matrix.target}}
264+
cc -o zpipe zpipe.c target/${{matrix.target}}/release/deps/liblibz_rs_sys_cdylib.so
265+
./zpipe < Cargo.toml | ./zpipe -d > out.txt
266+
cmp -s Cargo.toml out.txt
267+
- name: "cdylib: rust-allocator"
268+
working-directory: libz-rs-sys-cdylib
269+
run: |
270+
cargo build --release --target ${{matrix.target}} --no-default-features --features="rust-allocator"
271+
cc -o zpipe zpipe.c target/${{matrix.target}}/release/deps/liblibz_rs_sys_cdylib.so
272+
./zpipe < Cargo.toml | ./zpipe -d > out.txt
273+
cmp -s Cargo.toml out.txt
274+
- name: "cdylib: no_std"
275+
working-directory: libz-rs-sys-cdylib
276+
run: |
277+
cargo build --release --target ${{matrix.target}} --no-default-features
278+
cc -o zpipe_no_std zpipe_no_std.c target/${{matrix.target}}/release/deps/liblibz_rs_sys_cdylib.so
279+
./zpipe_no_std < Cargo.toml | ./zpipe_no_std -d > out.txt
280+
cmp -s Cargo.toml out.txt
281+
- name: "cdylib: custom-prefix"
282+
working-directory: libz-rs-sys-cdylib
283+
env:
284+
LIBZ_RS_SYS_PREFIX: "MY_CUSTOM_PREFIX_"
285+
run: |
286+
cargo build --release --target ${{matrix.target}} --features=custom-prefix
287+
objdump target/${{matrix.target}}/release/deps/liblibz_rs_sys_cdylib.so | grep -q "MY_CUSTOM_PREFIX_uncompress" || (echo "symbol not found!" && exit 1)
274288
275289
miri:
276290
name: "Miri"
@@ -285,8 +299,8 @@ jobs:
285299
uses: taiki-e/install-action@56ab7930c591507f833cbaed864d201386d518a8
286300
with:
287301
tool: cargo-nextest
288-
- name: Test public C api with NULL arguments
302+
- name: Test public C api with NULL arguments
289303
run: "cargo +nightly miri nextest run -j4 -p test-libz-rs-sys null::"
290304
env:
291305
RUSTFLAGS: "-Ctarget-feature=+avx2"
292-
MIRIFLAGS: "-Zmiri-tree-borrows"
306+
MIRIFLAGS: "-Zmiri-tree-borrows"

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ members = [
55
"test-libz-rs-sys",
66
"dynamic-libz-sys",
77
]
8-
exclude = [ ]
8+
exclude = [
9+
"libz-rs-sys-cdylib",
10+
]
911

1012
# Without the `-p` flag, cargo ignores `--no-default-features` when you have a
1113
# workspace, and without `resolver = "2"` here, you can't use `-p` like this.

libz-rs-sys-cdylib/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "libz-rs-sys-cdylib"
3+
readme = "README.md"
4+
5+
[lib]
6+
crate-type=["cdylib"]
7+
8+
[features]
9+
default = ["c-allocator"] # when used as a cdylib crate, use the c allocator
10+
c-allocator = ["libz-rs-sys/c-allocator"] # by default, use malloc/free for memory allocation
11+
rust-allocator = ["libz-rs-sys/rust-allocator", "libz-rs-sys/std"] # by default, use the rust global alloctor for memory allocation
12+
custom-prefix = ["libz-rs-sys/custom-prefix"] # use the LIBZ_RS_SYS_PREFIX to prefix all exported symbols
13+
14+
[dependencies]
15+
libz-rs-sys = { path = "../libz-rs-sys", default-features = false }

libz-rs-sys-cdylib/README.md

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# `libz-rs-sys-cdylib`
2+
3+
Build `zlib-rs` as a drop-in replacement for the zlib dynamic library
4+
5+
```sh
6+
# build the cdylib
7+
cargo build --release
8+
9+
# the extension of a cdylib varies per platform
10+
cc zpipe.c target/release/libz-rs-sys-cdylib.so
11+
12+
# verify the implementation can compress and decompress our Cargo.toml
13+
./zpipe < Cargo.toml | ./zpipe -d
14+
```
15+
16+
## Features
17+
18+
### Allocators
19+
20+
We provide three options for the default allocator
21+
22+
**`c-allocator`**
23+
24+
```sh
25+
cargo build --release --no-default-features --features "c-allocator"
26+
```
27+
28+
Uses the libc `malloc` and `free` functions for memory allocation.
29+
30+
**`rust-allocator`**
31+
32+
```sh
33+
cargo build --release --no-default-features --features "std,rust-allocator"
34+
```
35+
Uses the rust standard library global allocator for memory allocation.
36+
37+
**no allocator**
38+
39+
```sh
40+
cargo build --release --no-default-features
41+
```
42+
43+
No allocator is configured automatically. This means that, before [`inflateInit_`] or [`deflateInit_`] are called,
44+
the user must set the `zalloc` and `zfree` fields of the `z_stream` to valid allocation and deallocation functions,
45+
and the `opaque` field to either `NULL` or a pointer expected by the (de)allocation functions.
46+
47+
If no allocator is configured, the initialization functions will return `Z_STREAM_ERROR`.
48+
49+
### Symbol Prefix
50+
51+
Symbols in C programs all live in the same namespace. A common solution to prevent names from clashing is to prefix
52+
all of a library's symbols with a prefix. We support prefixing the name at build time with the `custom-prefix` feature
53+
flag. When enabled, the value of the `LIBZ_RS_SYS_PREFIX` is used as a prefix for all exported symbols. For example:
54+
55+
```ignore
56+
> LIBZ_RS_SYS_PREFIX="MY_CUSTOM_PREFIX_" cargo build --release --features=custom-prefix
57+
58+
Compiling libz-rs-sys v0.2.1 (/home/folkertdev/rust/zlib-rs/libz-rs-sys)
59+
Compiling libz-rs-sys-cdylib v0.2.1 (/home/folkertdev/rust/zlib-rs/libz-rs-sys-cdylib)
60+
Finished `release` profile [optimized + debuginfo] target(s) in 0.16s
61+
> objdump -tT ../target/release/liblibz_rs_sys_cdylib.so | grep "uncompress"
62+
00000000000758e0 l O .got 0000000000000000 _ZN7zlib_rs7inflate10uncompress17hda65e03b54919c40E$got
63+
0000000000025da0 l F .text 000000000000029a _ZN7zlib_rs7inflate10uncompress17hda65e03b54919c40E
64+
000000000001d700 g F .text 0000000000000051 MY_CUSTOM_PREFIX_uncompress
65+
000000000001d700 g DF .text 0000000000000051 Base MY_CUSTOM_PREFIX_uncompress
66+
```
67+
68+
### `![no_std]`
69+
70+
The dynamic library can be built without the rust `std` crate, e.g. for embedded devices that don't support it. Note that
71+
the default rust allocator won't work in this case. the c allocator, or no default allocator still do. On embedded it is
72+
most common to provide a custom allocator that "allocates" into a custom array.

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

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
extern crate libz_rs_sys;
2+
3+
pub use libz_rs_sys::*;

libz-rs-sys-cdylib/zpipe.c

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/* zpipe.c: example of proper use of zlib's inflate() and deflate()
2+
Not copyrighted -- provided to the public domain
3+
Version 1.4 11 December 2005 Mark Adler */
4+
5+
/* Version history:
6+
1.0 30 Oct 2004 First version
7+
1.1 8 Nov 2004 Add void casting for unused return values
8+
Use switch statement for inflate() return values
9+
1.2 9 Nov 2004 Add assertions to document zlib guarantees
10+
1.3 6 Apr 2005 Remove incorrect assertion in inf()
11+
1.4 11 Dec 2005 Add hack to avoid MSDOS end-of-line conversions
12+
Avoid some compiler warnings for input and output buffers
13+
*/
14+
15+
#include <stdio.h>
16+
#include <string.h>
17+
#include <assert.h>
18+
#include "zlib.h"
19+
20+
#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
21+
# include <fcntl.h>
22+
# include <io.h>
23+
# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
24+
#else
25+
# define SET_BINARY_MODE(file)
26+
#endif
27+
28+
#define CHUNK 16384
29+
30+
/* Compress from file source to file dest until EOF on source.
31+
def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
32+
allocated for processing, Z_STREAM_ERROR if an invalid compression
33+
level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
34+
version of the library linked do not match, or Z_ERRNO if there is
35+
an error reading or writing the files. */
36+
int def(FILE *source, FILE *dest, int level)
37+
{
38+
int ret, flush;
39+
unsigned have;
40+
z_stream strm;
41+
unsigned char in[CHUNK];
42+
unsigned char out[CHUNK];
43+
44+
/* allocate deflate state */
45+
strm.zalloc = Z_NULL;
46+
strm.zfree = Z_NULL;
47+
strm.opaque = Z_NULL;
48+
ret = deflateInit(&strm, level);
49+
if (ret != Z_OK)
50+
return ret;
51+
52+
/* compress until end of file */
53+
do {
54+
strm.avail_in = fread(in, 1, CHUNK, source);
55+
if (ferror(source)) {
56+
(void)deflateEnd(&strm);
57+
return Z_ERRNO;
58+
}
59+
flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
60+
strm.next_in = in;
61+
62+
/* run deflate() on input until output buffer not full, finish
63+
compression if all of source has been read in */
64+
do {
65+
strm.avail_out = CHUNK;
66+
strm.next_out = out;
67+
ret = deflate(&strm, flush); /* no bad return value */
68+
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
69+
have = CHUNK - strm.avail_out;
70+
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
71+
(void)deflateEnd(&strm);
72+
return Z_ERRNO;
73+
}
74+
} while (strm.avail_out == 0);
75+
assert(strm.avail_in == 0); /* all input will be used */
76+
77+
/* done when last data in file processed */
78+
} while (flush != Z_FINISH);
79+
assert(ret == Z_STREAM_END); /* stream will be complete */
80+
81+
/* clean up and return */
82+
(void)deflateEnd(&strm);
83+
return Z_OK;
84+
}
85+
86+
/* Decompress from file source to file dest until stream ends or EOF.
87+
inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
88+
allocated for processing, Z_DATA_ERROR if the deflate data is
89+
invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
90+
the version of the library linked do not match, or Z_ERRNO if there
91+
is an error reading or writing the files. */
92+
int inf(FILE *source, FILE *dest)
93+
{
94+
int ret;
95+
unsigned have;
96+
z_stream strm;
97+
unsigned char in[CHUNK];
98+
unsigned char out[CHUNK];
99+
100+
/* allocate inflate state */
101+
strm.zalloc = Z_NULL;
102+
strm.zfree = Z_NULL;
103+
strm.opaque = Z_NULL;
104+
strm.avail_in = 0;
105+
strm.next_in = Z_NULL;
106+
ret = inflateInit(&strm);
107+
if (ret != Z_OK)
108+
return ret;
109+
110+
/* decompress until deflate stream ends or end of file */
111+
do {
112+
strm.avail_in = fread(in, 1, CHUNK, source);
113+
if (ferror(source)) {
114+
(void)inflateEnd(&strm);
115+
return Z_ERRNO;
116+
}
117+
if (strm.avail_in == 0)
118+
break;
119+
strm.next_in = in;
120+
121+
/* run inflate() on input until output buffer not full */
122+
do {
123+
strm.avail_out = CHUNK;
124+
strm.next_out = out;
125+
ret = inflate(&strm, Z_NO_FLUSH);
126+
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
127+
switch (ret) {
128+
case Z_NEED_DICT:
129+
ret = Z_DATA_ERROR; /* and fall through */
130+
case Z_DATA_ERROR:
131+
case Z_MEM_ERROR:
132+
(void)inflateEnd(&strm);
133+
return ret;
134+
}
135+
have = CHUNK - strm.avail_out;
136+
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
137+
(void)inflateEnd(&strm);
138+
return Z_ERRNO;
139+
}
140+
} while (strm.avail_out == 0);
141+
142+
/* done when inflate() says it's done */
143+
} while (ret != Z_STREAM_END);
144+
145+
/* clean up and return */
146+
(void)inflateEnd(&strm);
147+
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
148+
}
149+
150+
/* report a zlib or i/o error */
151+
void zerr(int ret)
152+
{
153+
fputs("zpipe: ", stderr);
154+
switch (ret) {
155+
case Z_ERRNO:
156+
if (ferror(stdin))
157+
fputs("error reading stdin\n", stderr);
158+
if (ferror(stdout))
159+
fputs("error writing stdout\n", stderr);
160+
break;
161+
case Z_STREAM_ERROR:
162+
fputs("invalid compression level\n", stderr);
163+
break;
164+
case Z_DATA_ERROR:
165+
fputs("invalid or incomplete deflate data\n", stderr);
166+
break;
167+
case Z_MEM_ERROR:
168+
fputs("out of memory\n", stderr);
169+
break;
170+
case Z_VERSION_ERROR:
171+
fputs("zlib version mismatch!\n", stderr);
172+
}
173+
}
174+
175+
/* compress or decompress from stdin to stdout */
176+
int main(int argc, char **argv)
177+
{
178+
int ret;
179+
180+
/* avoid end-of-line conversions */
181+
SET_BINARY_MODE(stdin);
182+
SET_BINARY_MODE(stdout);
183+
184+
/* do compression if no arguments */
185+
if (argc == 1) {
186+
ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
187+
if (ret != Z_OK)
188+
zerr(ret);
189+
return ret;
190+
}
191+
192+
/* do decompression if -d specified */
193+
else if (argc == 2 && strcmp(argv[1], "-d") == 0) {
194+
ret = inf(stdin, stdout);
195+
if (ret != Z_OK)
196+
zerr(ret);
197+
return ret;
198+
}
199+
200+
/* otherwise, report usage */
201+
else {
202+
fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr);
203+
return 1;
204+
}
205+
}

0 commit comments

Comments
 (0)