Skip to content

Commit ea07b27

Browse files
authored
add miniz support (#83)
* ci: run tests with miniz * read_chunk_bytes: do stricter arg check * update README, docs * update wrap to fix deprecation warning
1 parent 9b17930 commit ea07b27

File tree

7 files changed

+115
-78
lines changed

7 files changed

+115
-78
lines changed

.gitlab-ci.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ test:
1212
stage: test
1313
script:
1414
- CC=clang-7 CXX=clang++-7 meson -Ddev_build=true -Db_sanitize=address,undefined -Db_lundef=false clang_build
15-
- ninja -C clang_build test
16-
- ninja -C clang_build scan-build # this uses gcc due to a bug: https://github.com/mesonbuild/meson/issues/5716
15+
- cd clang_build
16+
- ninja test
17+
- ninja scan-build # this uses gcc due to a bug: https://github.com/mesonbuild/meson/issues/5716
18+
- meson configure -Duse_miniz=true
19+
- ninja test
20+
- cd ..
1721
- CC=gcc CXX=g++ meson -Ddev_build=true -Db_sanitize=address,undefined -Db_coverage=true gcc_build
1822
- cd gcc_build
1923
- ninja

README.md

+15-15
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,24 @@ it outperforms the reference implementation in common use cases.
2424

2525
## Features
2626

27-
| Feature | libspng | libpng | stb_image | lodepng |
28-
|--------------------------------------|---------|--------|-----------|---------|
29-
| Decode from stream |||||
30-
| Gamma correction |||||
31-
| No known security bugs<sup>[1]</sup> |||||
32-
| Progressive image read |||||
33-
| Parses all standard chunks |||||
34-
| Doesn't require zlib |* | |||
35-
| Encoding | * | |||
36-
| Animated PNG | * |** |||
27+
| Feature | libspng | libpng | stb_image | lodepng |
28+
|--------------------------------------|---------|--------------------|-----------|---------|
29+
| Decode from stream || |||
30+
| Gamma correction || |||
31+
| No known security bugs<sup>[1]</sup> || |||
32+
| Progressive image read || |||
33+
| Parses all standard chunks || |||
34+
| Doesn't require zlib<sup>[2]</sup> | | |||
35+
| Encoding | Planned | |||
36+
| Animated PNG | Planned |<sup>[3]</sup> |||
3737

38-
[1] The project is fuzz tested on [OSS-Fuzz](https://github.com/google/oss-fuzz) and vulnerabilities are fixed before they become public.
38+
<sup>[1]</sup> The project is fuzz tested on [OSS-Fuzz](https://github.com/google/oss-fuzz) and vulnerabilities are fixed before they become public.
3939

40-
\* Work in progress
40+
<sup>[2]</sup> Building with miniz is [supported](docs/build.md#miniz).
4141

42-
\*\* With a 3rd party patch
42+
<sup>[3]</sup> With a 3rd party patch
4343

44-
## Getting spng
44+
## Getting libspng
4545

4646
Download the [latest release](https://libspng.org/download) and include `spng.c/spng.h` in your project,
4747
you can also build with CMake or Meson. Refer to the [documentation](https://libspng.org/docs) for details.
@@ -65,7 +65,7 @@ spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, 0);
6565
spng_ctx_free(ctx);
6666
```
6767
68-
See [example.c](https://github.com/randy408/libspng/blob/v0.5.0/examples/example.c).
68+
See [example.c](https://github.com/randy408/libspng/blob/v0.6.0/examples/example.c).
6969
7070
## License
7171

docs/build.md

+14
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ any configuration, intrinsics are enabled by default.
3737

3838
# Build options
3939

40+
## Optimizations
41+
4042
Architecture-specific intrinsics are enabled by default,
4143
this can be disabled with the `SPNG_DISABLE_OPT` compiler option.
4244

@@ -56,6 +58,18 @@ for multiple instruction sets, this is enabled by the
5658
of GCC and glibc.
5759
For the Meson project this is always enabled if the target supports it.
5860

61+
## miniz
62+
63+
[miniz](https://github.com/richgel999/miniz) is a single source file replacement for zlib,
64+
linking against miniz allows libspng to be embedded into a project with just
65+
four files: `spng.c`, `miniz.c` and their headers.
66+
67+
For building with miniz use the `SPNG_USE_MINIZ` compiler option,
68+
this handles some minor issues with the API.
69+
The Meson build option for this is `use_miniz`.
70+
Performance is mostly identical, slightly better in some cases
71+
compared to stock zlib.
72+
5973
# Profile-guided optimization
6074

6175
[Profile-guided optimization (PGO)](https://clang.llvm.org/docs/UsersManual.html#profile-guided-optimization)

meson.build

+8-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@ cc = meson.get_compiler('c')
44

55
spng_inc = include_directories('.')
66

7-
if cc.get_define('__ANDROID__') != ''
8-
zlib_dep = cc.find_library('z')
7+
if get_option('use_miniz') == true
8+
add_project_arguments('-DSPNG_USE_MINIZ', language : 'c')
9+
zlib_dep = dependency('miniz', fallback : [ 'miniz', 'miniz_dep'])
910
else
10-
zlib_dep = dependency('zlib', fallback : ['zlib', 'zlib_dep'], static : get_option('static_zlib'))
11+
if cc.get_define('__ANDROID__') != ''
12+
zlib_dep = cc.find_library('z')
13+
else
14+
zlib_dep = dependency('zlib', fallback : ['zlib', 'zlib_dep'], static : get_option('static_zlib'))
15+
endif
1116
endif
1217

1318
m_dep = cc.find_library('m', required : false)

meson_options.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
option('dev_build', type : 'boolean', value : false, description : 'Enable the testsuite, requires libpng')
22
option('enable_opt', type : 'boolean', value : true, description : 'Enable architecture-specific optimizations')
3+
option('use_miniz', type : 'boolean', value : false, description : 'Compile with miniz instead of zlib, disables some features')
34
option('static_zlib', type : 'boolean', value : false, description : 'Link the static version of zlib')
45
option('benchmarks', type : 'boolean', value : false, description : 'Enable benchmarks, requires Git LFS')
56

spng.c

+68-55
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
#define SPNG_DISABLE_OPT
1313
#include "tests/framac_stubs.h"
1414
#else
15-
#include <zlib.h>
15+
#ifdef SPNG_USE_MINIZ
16+
#include <miniz.h>
17+
#else
18+
#include <zlib.h>
19+
#endif
1620
#endif
1721

1822
#ifdef SPNG_MULTITHREADING
@@ -317,7 +321,11 @@ static inline void spng__free(spng_ctx *ctx, void *ptr)
317321
ctx->alloc.free_fn(ptr);
318322
}
319323

324+
#if defined(SPNG_USE_MINIZ)
325+
static void *spng__zalloc(void *opaque, long unsigned items, long unsigned size)
326+
#else
320327
static void *spng__zalloc(void *opaque, unsigned items, unsigned size)
328+
#endif
321329
{
322330
spng_ctx *ctx = opaque;
323331

@@ -344,10 +352,10 @@ static int spng__inflate_init(spng_ctx *ctx)
344352

345353
if(inflateInit(&ctx->zstream) != Z_OK) return SPNG_EZLIB;
346354

347-
#if ZLIB_VERNUM >= 0x1290
355+
#if ZLIB_VERNUM >= 0x1290 && !defined(SPNG_USE_MINIZ)
348356
if(inflateValidate(&ctx->zstream, ctx->flags & SPNG_CTX_IGNORE_ADLER32)) return SPNG_EZLIB;
349-
#else
350-
#warning "zlib >= 1.2.11 is required for SPNG_CTX_IGNORE_ADLER32"
357+
#else /* This requires zlib >= 1.2.11 */
358+
#warning "inflateValidate() not available, SPNG_CTX_IGNORE_ADLER32 will be ignored"
351359
#endif
352360

353361
return 0;
@@ -595,7 +603,7 @@ static inline int read_header(spng_ctx *ctx, int *discard)
595603
static int read_chunk_bytes(spng_ctx *ctx, uint32_t bytes)
596604
{
597605
if(ctx == NULL) return 1;
598-
if(!bytes) return 0;
606+
if(!ctx->cur_chunk_bytes_left || !bytes) return 1;
599607
if(bytes > ctx->cur_chunk_bytes_left) return 1; /* XXX: more specific error? */
600608

601609
int ret;
@@ -619,7 +627,7 @@ static int read_chunk_bytes(spng_ctx *ctx, uint32_t bytes)
619627
static int read_chunk_bytes2(spng_ctx *ctx, void *out, uint32_t bytes)
620628
{
621629
if(ctx == NULL) return 1;
622-
if(!bytes) return 0;
630+
if(!ctx->cur_chunk_bytes_left || !bytes) return 1;
623631
if(bytes > ctx->cur_chunk_bytes_left) return 1; /* XXX: more specific error? */
624632

625633
int ret;
@@ -726,49 +734,54 @@ static int spng__inflate_stream(spng_ctx *ctx, char **out, size_t *len, int extr
726734
stream->avail_out = size;
727735
stream->next_out = buf;
728736

729-
do
737+
while(ret != Z_STREAM_END)
730738
{
731-
if(ret != Z_OK)
732-
{
733-
spng__free(ctx, buf);
734-
return SPNG_EZLIB;
735-
}
739+
ret = inflate(stream, 0);
736740

737-
if(!stream->avail_out)
738-
{
739-
/* overflow or reached chunk/cache limit */
740-
if( (2 > SIZE_MAX / size) || (size > max / 2) ) goto mem;
741+
if(ret == Z_OK) continue;
741742

742-
size *= 2;
743+
if(ret == Z_STREAM_END) break;
743744

744-
t = spng__realloc(ctx, buf, size);
745-
if(t == NULL) goto mem;
745+
if(ret == Z_BUF_ERROR)
746+
{
747+
if(!stream->avail_out) /* Resize buffer */
748+
{
749+
/* overflow or reached chunk/cache limit */
750+
if( (2 > SIZE_MAX / size) || (size > max / 2) ) goto mem;
746751

747-
buf = t;
752+
size *= 2;
748753

749-
stream->avail_out = size / 2;
750-
stream->next_out = (unsigned char*)buf + size / 2;
751-
}
754+
t = spng__realloc(ctx, buf, size);
755+
if(t == NULL) goto mem;
752756

753-
if(!stream->avail_in) /* Read more chunk bytes */
754-
{
755-
read_size = ctx->cur_chunk_bytes_left;
756-
if(ctx->streaming && read_size > SPNG_READ_SIZE) read_size = SPNG_READ_SIZE;
757+
buf = t;
757758

758-
ret = read_chunk_bytes(ctx, read_size);
759-
if(ret)
760-
{
761-
spng__free(ctx, buf);
762-
return ret;
759+
stream->avail_out = size / 2;
760+
stream->next_out = (unsigned char*)buf + size / 2;
763761
}
764762

765-
stream->avail_in = read_size;
766-
stream->next_in = ctx->data;
767-
}
763+
if(!stream->avail_in) /* Read more chunk bytes */
764+
{
765+
read_size = ctx->cur_chunk_bytes_left;
766+
if(ctx->streaming && read_size > SPNG_READ_SIZE) read_size = SPNG_READ_SIZE;
768767

769-
ret = inflate(stream, Z_SYNC_FLUSH);
768+
ret = read_chunk_bytes(ctx, read_size);
769+
if(ret)
770+
{
771+
spng__free(ctx, buf);
772+
return ret;
773+
}
770774

771-
}while(ret != Z_STREAM_END);
775+
stream->avail_in = read_size;
776+
stream->next_in = ctx->data;
777+
}
778+
}
779+
else
780+
{
781+
spng__free(ctx, buf);
782+
return SPNG_EZLIB;
783+
}
784+
}
772785

773786
size = stream->total_out;
774787

@@ -833,33 +846,33 @@ static int read_scanline_bytes(spng_ctx *ctx, unsigned char *dest, size_t len)
833846
{
834847
if(ctx == NULL || dest == NULL) return 1;
835848

836-
int ret;
849+
int ret = Z_OK;
837850
uint32_t bytes_read;
838851

839-
ctx->zstream.avail_out = len;
840-
ctx->zstream.next_out = dest;
852+
z_stream *zstream = &ctx->zstream;
841853

842-
while(ctx->zstream.avail_out != 0)
854+
zstream->avail_out = len;
855+
zstream->next_out = dest;
856+
857+
while(zstream->avail_out != 0)
843858
{
844-
if(ctx->zstream.avail_in == 0) /* Need more IDAT bytes */
859+
ret = inflate(&ctx->zstream, 0);
860+
861+
if(ret == Z_OK) continue;
862+
863+
if(ret == Z_STREAM_END) /* Reached an end-marker */
864+
{
865+
if(zstream->avail_out != 0) return SPNG_EIDAT_TOO_SHORT;
866+
}
867+
else if(ret == Z_BUF_ERROR) /* Read more IDAT bytes */
845868
{
846869
ret = read_idat_bytes(ctx, &bytes_read);
847870
if(ret) return ret;
848871

849-
ctx->zstream.avail_in = bytes_read;
850-
ctx->zstream.next_in = ctx->data;
851-
}
852-
853-
ret = inflate(&ctx->zstream, Z_SYNC_FLUSH);
854-
855-
if(ret != Z_OK)
856-
{
857-
if(ret == Z_STREAM_END) /* zlib reached an end-marker */
858-
{
859-
if(ctx->zstream.avail_out != 0) return SPNG_EIDAT_TOO_SHORT;
860-
}
861-
else if(ret != Z_BUF_ERROR) return SPNG_EIDAT_STREAM;
872+
zstream->avail_in = bytes_read;
873+
zstream->next_in = ctx->data;
862874
}
875+
else return SPNG_EIDAT_STREAM;
863876
}
864877

865878
return 0;

subprojects/miniz.wrap

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ source_url = https://github.com/richgel999/miniz/releases/download/2.1.0/miniz-2
66
source_filename = miniz-2.1.0.zip
77
source_hash = d133132721ad5efbcda2507699d44c54b0da5e31379e4ff049d78d6b1a571f0d
88

9-
patch_url = https://wrapdb.mesonbuild.com/v1/projects/miniz/2.1.0/1/get_zip
10-
patch_filename = miniz-2.1.0-1-wrap.zip
11-
patch_hash = 1bc43e0dd59490d12020e45d16f1b5502f2f1164c2cabf787f5d3082d12e895c
9+
patch_url = https://wrapdb.mesonbuild.com/v1/projects/miniz/2.1.0/2/get_zip
10+
patch_filename = miniz-2.1.0-2-wrap.zip
11+
patch_hash = 529e05709a00e5b0fd1d27669a6b4f14b9b32da82d4f6f5669933657842610b1

0 commit comments

Comments
 (0)