hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 107ca216d414e61c79e00db80585e146c1f80310
parent 5c0d392f2678b91416aaf3f330dc769e0d5d3086
Author: Eyal Sawady <ecs@d2evs.net>
Date:   Tue, 30 Mar 2021 05:34:58 -0400

compress::zlib: new module

Diffstat:
Acompress/zlib/data+test.ha | 1+
Acompress/zlib/reader.ha | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 15+++++++++++++++
Mstdlib.mk | 27+++++++++++++++++++++++++++
4 files changed, 182 insertions(+), 0 deletions(-)

diff --git a/compress/zlib/data+test.ha b/compress/zlib/data+test.ha @@ -0,0 +1 @@ +const vectors: [](*[]u8, *[]u8) = []; diff --git a/compress/zlib/reader.ha b/compress/zlib/reader.ha @@ -0,0 +1,139 @@ +use bufio; +use bytes; +use compress::flate; +use endian; +use errors; +use fmt; +use hash::adler32; +use hash; +use io; + +def CM: u8 = 0b00001111; +def CINFO: u8 = 0b11110000; + +def FCHECK: u8 = 0b00011111; +def FDICT: u8 = 0b00100000; +def FLEVEL: u8 = 0b11000000; + +type reader = struct { + stream: io::stream, + source: *io::stream, + flate: *io::stream, + hash: *hash::hash, +}; + +type decompress_err = enum { + HEADER, + CHECKSUM, + EOF, + DICT, +}; + +fn opaque_strerror( + data: *errors::opaque_data +) const str = switch (*(data: *decompress_err)) { + decompress_err::HEADER => "Invalid zlib header", + decompress_err::CHECKSUM => "Invalid zlib checksum", + decompress_err::EOF => "Unexpected EOF", + decompress_err::DICT => "Invalid dictionary", + * => abort(), +}; + +fn wraperror(err: decompress_err) errors::opaque = { + static assert(size(decompress_err) <= size(error::opaque_data)); + let wrapped = errors::opaque { strerror = &opaque_strerror, ... }; + let myptr = &wrapped.data: *decompress_err; + *myptr = err; + return wrapped; +}; + +fn verifysum(s: *reader) (io::EOF | io::error) = { + let hash: [4]u8 = [0...]; + for (let n = 0z; n < len(hash)) { + match (io::read(s.source, hash[n..])?) { + io::EOF => return wraperror(decompress_err::EOF), + z: size => n += z, + }; + }; + let checksum = hash::sum(s.hash); + defer free(checksum); + return if (bytes::equal(checksum, hash)) io::EOF + else wraperror(decompress_err::CHECKSUM); +}; + +fn read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { + let s = s: *reader; + match (io::read(s.flate, buf)?) { + io::EOF => return verifysum(s), + z: size => buf = buf[..z], + }; + hash::write(s.hash, buf); +}; + +fn close(s: *io::stream) void = { + const s = s: *reader; + io::close(s.flate); + hash::close(s.hash); +}; + +// Creates a stream which decompresses zlib (RFC 1950) data. +export fn decompress(s: *io::stream) (*io::stream | io::error) = { + let buf: [2]u8 = [0...]; + for (let n = 0z; n < len(buf)) { + match (io::read(s, buf[n..])?) { + io::EOF => return wraperror(decompress_err::EOF), + z: size => n += z, + }; + }; + if (buf[0] & CM != 8) { + return wraperror(decompress_err::HEADER); + }; + // XXX: Should we tell compress::flate to allocate a smaller buffer on + // CINFO < 7? + if ((buf[0] & CINFO) >> 4 > 7) { + return wraperror(decompress_err::HEADER); + }; + if ((buf[0] << 8 | buf[1]) % 31 != 0) { + // Invalid FCHECK + return wraperror(decompress_err::HEADER); + }; + if ((buf[1] & FDICT == 1)) { + // TODO: Allow the caller to pass in a list of dictionaries to + // be tried + return wraperror(decompress_err::DICT); + }; + return alloc(reader { + stream = io::stream { + reader = &read, + closer = &close, + ... + }, + source = s, + flate = flate::inflate(s), + hash = adler32::adler32(), + ... + }): *io::stream; +}; + +@test fn decompress() void = { + for (let i = 1z; i < len(vectors); i += 1) { + let in = bufio::fixed(*vectors[i].1, io::mode::READ); + let out = bufio::dynamic(io::mode::WRITE); + let d = match (decompress(in)) { + s: *io::stream => s, + e: io::error => { + fmt::errorln(io::strerror(e)); + abort(); + }, + }; + match (io::copy(out, d)) { + size => void, + e: io::error => { + fmt::errorfln("vector {}: {}", i, io::strerror(e)); + abort(); + }, + }; + let s = bufio::finish(out); + assert(bytes::equal(s, *vectors[i].0)); + }; +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -131,6 +131,20 @@ compress_flate() { gen_ssa compress::flate bufio bytes endian errors io fmt } +compress_zlib() { + if [ $testing -eq 0 ] + then + gen_srcs compress::zlib \ + reader.ha + else + gen_srcs compress::zlib \ + data+test.ha \ + reader.ha + fi + gen_ssa compress::zlib bufio bytes compress::flate endian errors hash \ + hash::adler32 io +} + crypto_math() { gen_srcs crypto::math \ bits.ha @@ -614,6 +628,7 @@ modules="ascii bufio bytes compress_flate +compress_zlib crypto_math crypto_random crypto_md5 diff --git a/stdlib.mk b/stdlib.mk @@ -81,6 +81,9 @@ hare_stdlib_deps+=$(stdlib_bytes) stdlib_compress_flate=$(HARECACHE)/compress/flate/compress_flate.o hare_stdlib_deps+=$(stdlib_compress_flate) +stdlib_compress_zlib=$(HARECACHE)/compress/zlib/compress_zlib.o +hare_stdlib_deps+=$(stdlib_compress_zlib) + stdlib_crypto_math=$(HARECACHE)/crypto/math/crypto_math.o hare_stdlib_deps+=$(stdlib_crypto_math) @@ -268,6 +271,16 @@ $(HARECACHE)/compress/flate/compress_flate.ssa: $(stdlib_compress_flate_srcs) $( @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncompress::flate \ -t$(HARECACHE)/compress/flate/compress_flate.td $(stdlib_compress_flate_srcs) +# compress::zlib +stdlib_compress_zlib_srcs= \ + $(STDLIB)/compress/zlib/reader.ha + +$(HARECACHE)/compress/zlib/compress_zlib.ssa: $(stdlib_compress_zlib_srcs) $(stdlib_rt) $(stdlib_bufio) $(stdlib_bytes) $(stdlib_compress_flate) $(stdlib_endian) $(stdlib_errors) $(stdlib_hash) $(stdlib_hash_adler32) $(stdlib_io) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/compress/zlib + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncompress::zlib \ + -t$(HARECACHE)/compress/zlib/compress_zlib.td $(stdlib_compress_zlib_srcs) + # crypto::math stdlib_crypto_math_srcs= \ $(STDLIB)/crypto/math/bits.ha @@ -907,6 +920,9 @@ hare_testlib_deps+=$(testlib_bytes) testlib_compress_flate=$(TESTCACHE)/compress/flate/compress_flate.o hare_testlib_deps+=$(testlib_compress_flate) +testlib_compress_zlib=$(TESTCACHE)/compress/zlib/compress_zlib.o +hare_testlib_deps+=$(testlib_compress_zlib) + testlib_crypto_math=$(TESTCACHE)/crypto/math/crypto_math.o hare_testlib_deps+=$(testlib_crypto_math) @@ -1094,6 +1110,17 @@ $(TESTCACHE)/compress/flate/compress_flate.ssa: $(testlib_compress_flate_srcs) $ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncompress::flate \ -t$(TESTCACHE)/compress/flate/compress_flate.td $(testlib_compress_flate_srcs) +# compress::zlib +testlib_compress_zlib_srcs= \ + $(STDLIB)/compress/zlib/data+test.ha \ + $(STDLIB)/compress/zlib/reader.ha + +$(TESTCACHE)/compress/zlib/compress_zlib.ssa: $(testlib_compress_zlib_srcs) $(testlib_rt) $(testlib_bufio) $(testlib_bytes) $(testlib_compress_flate) $(testlib_endian) $(testlib_errors) $(testlib_hash) $(testlib_hash_adler32) $(testlib_io) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/compress/zlib + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncompress::zlib \ + -t$(TESTCACHE)/compress/zlib/compress_zlib.td $(testlib_compress_zlib_srcs) + # crypto::math testlib_crypto_math_srcs= \ $(STDLIB)/crypto/math/bits.ha