0016-dlang-update-zlib-binding.patch (13412B)
1 From b2e1bf216169f470ced86f1f3e95c89debf606cd Mon Sep 17 00:00:00 2001 2 From: Ariadne Conill <ariadne@dereferenced.org> 3 Date: Fri, 21 Aug 2020 06:57:51 +0000 4 Subject: [PATCH] dlang: update zlib binding 5 6 --- 7 libphobos/src/std/zlib.d | 266 ++++++++++++++++++++++++++++----------- 8 1 file changed, 196 insertions(+), 70 deletions(-) 9 10 diff --git a/libphobos/src/std/zlib.d b/libphobos/src/std/zlib.d 11 index e6cce240fd5..bd2fe37ebec 100644 12 --- a/libphobos/src/std/zlib.d 13 +++ b/libphobos/src/std/zlib.d 14 @@ -1,7 +1,7 @@ 15 // Written in the D programming language. 16 17 /** 18 - * Compress/decompress data using the $(HTTP www._zlib.net, _zlib library). 19 + * Compress/decompress data using the $(HTTP www.zlib.net, zlib library). 20 * 21 * Examples: 22 * 23 @@ -43,12 +43,12 @@ 24 * References: 25 * $(HTTP en.wikipedia.org/wiki/Zlib, Wikipedia) 26 * 27 - * Copyright: Copyright Digital Mars 2000 - 2011. 28 + * Copyright: Copyright The D Language Foundation 2000 - 2011. 29 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 30 * Authors: $(HTTP digitalmars.com, Walter Bright) 31 - * Source: $(PHOBOSSRC std/_zlib.d) 32 + * Source: $(PHOBOSSRC std/zlib.d) 33 */ 34 -/* Copyright Digital Mars 2000 - 2011. 35 +/* Copyright The D Language Foundation 2000 - 2011. 36 * Distributed under the Boost Software License, Version 1.0. 37 * (See accompanying file LICENSE_1_0.txt or copy at 38 * http://www.boost.org/LICENSE_1_0.txt) 39 @@ -75,9 +75,9 @@ enum 40 41 class ZlibException : Exception 42 { 43 - this(int errnum) 44 - { string msg; 45 - 46 + private static string getmsg(int errnum) nothrow @nogc pure @safe 47 + { 48 + string msg; 49 switch (errnum) 50 { 51 case Z_STREAM_END: msg = "stream end"; break; 52 @@ -90,7 +90,12 @@ class ZlibException : Exception 53 case Z_VERSION_ERROR: msg = "version error"; break; 54 default: msg = "unknown error"; break; 55 } 56 - super(msg); 57 + return msg; 58 + } 59 + 60 + this(int errnum) 61 + { 62 + super(getmsg(errnum)); 63 } 64 } 65 66 @@ -104,7 +109,7 @@ class ZlibException : Exception 67 * buf = buffer containing input data 68 * 69 * Returns: 70 - * A $(D uint) checksum for the provided input data and starting checksum 71 + * A `uint` checksum for the provided input data and starting checksum 72 * 73 * See_Also: 74 * $(LINK http://en.wikipedia.org/wiki/Adler-32) 75 @@ -147,7 +152,7 @@ uint adler32(uint adler, const(void)[] buf) 76 * buf = buffer containing input data 77 * 78 * Returns: 79 - * A $(D uint) checksum for the provided input data and starting checksum 80 + * A `uint` checksum for the provided input data and starting checksum 81 * 82 * See_Also: 83 * $(LINK http://en.wikipedia.org/wiki/Cyclic_redundancy_check) 84 @@ -191,13 +196,14 @@ uint crc32(uint crc, const(void)[] buf) 85 ubyte[] compress(const(void)[] srcbuf, int level) 86 in 87 { 88 - assert(-1 <= level && level <= 9); 89 + assert(-1 <= level && level <= 9, "Compression level needs to be within [-1, 9]."); 90 } 91 -body 92 +do 93 { 94 import core.memory : GC; 95 + import std.array : uninitializedArray; 96 auto destlen = srcbuf.length + ((srcbuf.length + 1023) / 1024) + 12; 97 - auto destbuf = new ubyte[destlen]; 98 + auto destbuf = uninitializedArray!(ubyte[])(destlen); 99 auto err = etc.c.zlib.compress2(destbuf.ptr, &destlen, cast(ubyte *) srcbuf.ptr, srcbuf.length, level); 100 if (err) 101 { 102 @@ -276,7 +282,7 @@ void[] uncompress(const(void)[] srcbuf, size_t destlen = 0u, int winbits = 15) 103 throw new ZlibException(err); 104 } 105 } 106 - assert(0); 107 + assert(0, "Unreachable code"); 108 } 109 110 @system unittest 111 @@ -370,9 +376,9 @@ class Compress 112 this(int level, HeaderFormat header = HeaderFormat.deflate) 113 in 114 { 115 - assert(1 <= level && level <= 9); 116 + assert(1 <= level && level <= 9, "Legal compression level are in [1, 9]."); 117 } 118 - body 119 + do 120 { 121 this.level = level; 122 this.gzip = header == HeaderFormat.gzip; 123 @@ -406,6 +412,7 @@ class Compress 124 const(void)[] compress(const(void)[] buf) 125 { 126 import core.memory : GC; 127 + import std.array : uninitializedArray; 128 int err; 129 ubyte[] destbuf; 130 131 @@ -420,7 +427,7 @@ class Compress 132 inited = 1; 133 } 134 135 - destbuf = new ubyte[zs.avail_in + buf.length]; 136 + destbuf = uninitializedArray!(ubyte[])(zs.avail_in + buf.length); 137 zs.next_out = destbuf.ptr; 138 zs.avail_out = to!uint(destbuf.length); 139 140 @@ -461,9 +468,10 @@ class Compress 141 void[] flush(int mode = Z_FINISH) 142 in 143 { 144 - assert(mode == Z_FINISH || mode == Z_SYNC_FLUSH || mode == Z_FULL_FLUSH); 145 + assert(mode == Z_FINISH || mode == Z_SYNC_FLUSH || mode == Z_FULL_FLUSH, 146 + "Mode must be either Z_FINISH, Z_SYNC_FLUSH or Z_FULL_FLUSH."); 147 } 148 - body 149 + do 150 { 151 import core.memory : GC; 152 ubyte[] destbuf; 153 @@ -523,6 +531,7 @@ class UnCompress 154 z_stream zs; 155 int inited; 156 int done; 157 + bool inputEnded; 158 size_t destbufsize; 159 160 HeaderFormat format; 161 @@ -571,16 +580,16 @@ class UnCompress 162 const(void)[] uncompress(const(void)[] buf) 163 in 164 { 165 - assert(!done); 166 + assert(!done, "Buffer has been flushed."); 167 } 168 - body 169 + do 170 { 171 + if (inputEnded || !buf.length) 172 + return null; 173 + 174 import core.memory : GC; 175 + import std.array : uninitializedArray; 176 int err; 177 - ubyte[] destbuf; 178 - 179 - if (buf.length == 0) 180 - return null; 181 182 if (!inited) 183 { 184 @@ -598,26 +607,152 @@ class UnCompress 185 186 if (!destbufsize) 187 destbufsize = to!uint(buf.length) * 2; 188 - destbuf = new ubyte[zs.avail_in * 2 + destbufsize]; 189 - zs.next_out = destbuf.ptr; 190 - zs.avail_out = to!uint(destbuf.length); 191 - 192 - if (zs.avail_in) 193 - buf = zs.next_in[0 .. zs.avail_in] ~ cast(ubyte[]) buf; 194 + auto destbuf = uninitializedArray!(ubyte[])(destbufsize); 195 + size_t destFill; 196 197 zs.next_in = cast(ubyte*) buf.ptr; 198 zs.avail_in = to!uint(buf.length); 199 200 - err = inflate(&zs, Z_NO_FLUSH); 201 - if (err != Z_STREAM_END && err != Z_OK) 202 + while (true) 203 { 204 - GC.free(destbuf.ptr); 205 - error(err); 206 + auto oldAvailIn = zs.avail_in; 207 + 208 + zs.next_out = destbuf[destFill .. $].ptr; 209 + zs.avail_out = to!uint(destbuf.length - destFill); 210 + 211 + err = inflate(&zs, Z_NO_FLUSH); 212 + if (err == Z_STREAM_END) 213 + { 214 + inputEnded = true; 215 + break; 216 + } 217 + else if (err != Z_OK) 218 + { 219 + GC.free(destbuf.ptr); 220 + error(err); 221 + } 222 + else if (!zs.avail_in) 223 + break; 224 + 225 + /* 226 + According to the zlib manual inflate() stops when either there's 227 + no more data to uncompress or the output buffer is full 228 + So at this point, the output buffer is too full 229 + */ 230 + 231 + destFill = destbuf.length; 232 + 233 + if (destbuf.capacity) 234 + { 235 + if (destbuf.length < destbuf.capacity) 236 + destbuf.length = destbuf.capacity; 237 + else 238 + { 239 + auto newLength = GC.extend(destbuf.ptr, destbufsize, destbufsize); 240 + 241 + if (newLength && destbuf.length < destbuf.capacity) 242 + destbuf.length = destbuf.capacity; 243 + else 244 + destbuf.length += destbufsize; 245 + } 246 + } 247 + else 248 + destbuf.length += destbufsize; 249 } 250 + 251 destbuf.length = destbuf.length - zs.avail_out; 252 return destbuf; 253 } 254 255 + // Test for issues 3191 and 9505 256 + @system unittest 257 + { 258 + import std.algorithm.comparison; 259 + import std.array; 260 + import std.file; 261 + import std.zlib; 262 + 263 + // Data that can be easily compressed 264 + ubyte[1024] originalData; 265 + 266 + // This should yield a compression ratio of at least 1/2 267 + auto compressedData = compress(originalData, 9); 268 + assert(compressedData.length < originalData.length / 2, 269 + "The compression ratio is too low to accurately test this situation"); 270 + 271 + auto chunkSize = compressedData.length / 4; 272 + assert(chunkSize < compressedData.length, 273 + "The length of the compressed data is too small to accurately test this situation"); 274 + 275 + auto decompressor = new UnCompress(); 276 + ubyte[originalData.length] uncompressedData; 277 + ubyte[] reusedBuf; 278 + int progress; 279 + 280 + reusedBuf.length = chunkSize; 281 + 282 + for (int i = 0; i < compressedData.length; i += chunkSize) 283 + { 284 + auto len = min(chunkSize, compressedData.length - i); 285 + // simulate reading from a stream in small chunks 286 + reusedBuf[0 .. len] = compressedData[i .. i + len]; 287 + 288 + // decompress using same input buffer 289 + auto chunk = decompressor.uncompress(reusedBuf); 290 + assert(progress + chunk.length <= originalData.length, 291 + "The uncompressed result is bigger than the original data"); 292 + 293 + uncompressedData[progress .. progress + chunk.length] = cast(const ubyte[]) chunk[]; 294 + progress += chunk.length; 295 + } 296 + 297 + auto chunk = decompressor.flush(); 298 + assert(progress + chunk.length <= originalData.length, 299 + "The uncompressed result is bigger than the original data"); 300 + 301 + uncompressedData[progress .. progress + chunk.length] = cast(const ubyte[]) chunk[]; 302 + progress += chunk.length; 303 + 304 + assert(progress == originalData.length, 305 + "The uncompressed and the original data sizes differ"); 306 + assert(originalData[] == uncompressedData[], 307 + "The uncompressed and the original data differ"); 308 + } 309 + 310 + @system unittest 311 + { 312 + ubyte[1024] invalidData; 313 + auto decompressor = new UnCompress(); 314 + 315 + try 316 + { 317 + auto uncompressedData = decompressor.uncompress(invalidData); 318 + } 319 + catch (ZlibException e) 320 + { 321 + assert(e.msg == "data error"); 322 + return; 323 + } 324 + 325 + assert(false, "Corrupted data didn't result in an error"); 326 + } 327 + 328 + @system unittest 329 + { 330 + ubyte[2014] originalData = void; 331 + auto compressedData = compress(originalData, 9); 332 + 333 + auto decompressor = new UnCompress(); 334 + auto uncompressedData = decompressor.uncompress(compressedData ~ cast(ubyte[]) "whatever"); 335 + 336 + assert(originalData.length == uncompressedData.length, 337 + "The uncompressed and the original data sizes differ"); 338 + assert(originalData[] == uncompressedData[], 339 + "The uncompressed and the original data differ"); 340 + assert(!decompressor.uncompress("whatever").length, 341 + "Compression continued after the end"); 342 + } 343 + 344 /** 345 * Decompress and return any remaining data. 346 * The returned data should be appended to that returned by uncompress(). 347 @@ -626,49 +761,40 @@ class UnCompress 348 void[] flush() 349 in 350 { 351 - assert(!done); 352 + assert(!done, "Buffer has been flushed before."); 353 } 354 out 355 { 356 - assert(done); 357 + assert(done, "Flushing failed."); 358 } 359 - body 360 + do 361 { 362 - import core.memory : GC; 363 - ubyte[] extra; 364 - ubyte[] destbuf; 365 - int err; 366 - 367 done = 1; 368 - if (!inited) 369 - return null; 370 + return null; 371 + } 372 373 - L1: 374 - destbuf = new ubyte[zs.avail_in * 2 + 100]; 375 - zs.next_out = destbuf.ptr; 376 - zs.avail_out = to!uint(destbuf.length); 377 + /// Returns true if all input data has been decompressed and no further data 378 + /// can be decompressed (inflate() returned Z_STREAM_END) 379 + @property bool empty() const 380 + { 381 + return inputEnded; 382 + } 383 384 - err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH); 385 - if (err == Z_OK && zs.avail_out == 0) 386 - { 387 - extra ~= destbuf; 388 - goto L1; 389 - } 390 - if (err != Z_STREAM_END) 391 - { 392 - GC.free(destbuf.ptr); 393 - if (err == Z_OK) 394 - err = Z_BUF_ERROR; 395 - error(err); 396 - } 397 - destbuf = destbuf.ptr[0 .. zs.next_out - destbuf.ptr]; 398 - err = etc.c.zlib.inflateEnd(&zs); 399 - inited = 0; 400 - if (err) 401 - error(err); 402 - if (extra.length) 403 - destbuf = extra ~ destbuf; 404 - return destbuf; 405 + /// 406 + @system unittest 407 + { 408 + // some random data 409 + ubyte[1024] originalData = void; 410 + 411 + // append garbage data (or don't, this works in both cases) 412 + auto compressedData = cast(ubyte[]) compress(originalData) ~ cast(ubyte[]) "whatever"; 413 + 414 + auto decompressor = new UnCompress(); 415 + auto uncompressedData = decompressor.uncompress(compressedData); 416 + 417 + assert(uncompressedData[] == originalData[], 418 + "The uncompressed and the original data differ"); 419 + assert(decompressor.empty, "The UnCompressor reports not being done"); 420 } 421 } 422 423 -- 424 2.31.1 425