From 156006bb1a4eb8e041d3e3502d428a20392181b7 Mon Sep 17 00:00:00 2001 From: John Bowler Date: Sat, 19 Dec 2015 09:43:09 -0800 Subject: [PATCH] Added an accurate 'methodical' measure Also fix incorrect references to 'pngimage' in pngcp. Signed-off-by: John Bowler --- contrib/examples/pngcp.c | 12 +-- pngtest.png | Bin 6000 -> 5711 bytes pngwutil.c | 204 +++++++++++++++++++++++++++++++++++---- 3 files changed, 192 insertions(+), 24 deletions(-) diff --git a/contrib/examples/pngcp.c b/contrib/examples/pngcp.c index c775d18f9..647ecda1e 100644 --- a/contrib/examples/pngcp.c +++ b/contrib/examples/pngcp.c @@ -163,7 +163,7 @@ get_dp(png_structp pp) if (dp == NULL) { - fprintf(stderr, "pngimage: internal error (no display)\n"); + fprintf(stderr, "pngcp: internal error (no display)\n"); exit(99); /* prevents a crash */ } @@ -196,16 +196,16 @@ display_log(struct display *dp, error_level level, const char *fmt, ...) { case INFORMATION: lp = "information"; break; case LIBPNG_WARNING: lp = "warning(libpng)"; break; - case APP_WARNING: lp = "warning(pngimage)"; break; + case APP_WARNING: lp = "warning(pngcp)"; break; case APP_FAIL: lp = "error(continuable)"; break; case LIBPNG_ERROR: lp = "error(libpng)"; break; case LIBPNG_BUG: lp = "bug(libpng)"; break; - case APP_ERROR: lp = "error(pngimage)"; break; + case APP_ERROR: lp = "error(pngcp)"; break; case USER_ERROR: lp = "error(user)"; break; case INTERNAL_ERROR: /* anything unexpected is an internal error: */ case VERBOSE: case WARNINGS: case ERRORS: case QUIET: - default: lp = "bug(pngimage)"; break; + default: lp = "bug(pngcp)"; break; } fprintf(stderr, "%s: %s: %s", @@ -461,7 +461,7 @@ main(const int argc, const char * const * const argv) else if (name[0] == '-' && name[1] == '-') { - fprintf(stderr, "pngimage: %s: unknown option\n", name); + fprintf(stderr, "pngcp: %s: unknown option\n", name); return 99; } @@ -504,7 +504,7 @@ main(const int argc, const char * const * const argv) { int j; - printf("%s: pngimage", pass ? "PASS" : "FAIL"); + printf("%s: pngcp", pass ? "PASS" : "FAIL"); for (j=1; j*} zD8jyWPtWvuXWlclR@U_FOExKrx;*s&V_zXgNnC=#hi}pwR!d}zu{jv@pwKFLe!&~H z86|M?8m*Rj7?^Ncq6dXrvh9O{4{&~|zUe_>4hFU4n+Jt!v}k{|MEb=hR!f{5U@!`U zQ8)*KAO+6BU@!_jsOv9iCEA@vr`wxS@RY2^U~aw;jY3~vq%UYG+*8#(Roj=!+vw0J zasSJcgN^=PlSRpgY_&k6BmzCh5)3X?W!abUA30#M8nxud?H!v6t(N$Id&V_6%;9`J zd%&v?2cr;_z+ivW=0UaBJScn{9kyXOkZm^UHO3fQVtJ2-$KFPVuG&LyP!DBK7US*I zex(cp6IyU*wY+@#CUY>DJ97$Fqfr8B9XXJXpJ+=K=U`4{F~)KLTsu9161b(Jc@GMg zV9;ubMit|DI;!L}dg#`h5}2&UXcX>sDnW|NHhPqTC?{0<_5;Zo-F17 zO)XopI69316pjus)~CL^`JQaM5viw4B3!wlM_-=3!8sU|y)(S}KwTNpXw;Ht#u&3& zZrKCAYy5w&gbz%7`pbW`c#xT~5BQEB5k3rJ?BdZwGyI4cCEOD1RXke4wS}$mgCnoz z)f|!JgCl!qjIrzuMq5_3(-Mt(5Na7?OiS)22lzHROjd7oq{rO(87+B^R<{K-2iyFL z)e@E9+ue;BI~R(F;zyz_Bixe3sjf&Z`9|K)D1m>+?hS#3m0l zSu}0feLf4)w z-i>^X=glV%=%7r{5)FEgxI~NJ=U6;uEEclF;>(?u)ibsJSc|C^;HqB_Mq#uXCFih4i+f## zMepS)MpuH$=rr~g#uzIrEqg#Ed7Ic^6iyy4z5BiOkZZKnrvoOd+YjHHqc1A(py*LA zd>BNs8m*S?QCq50C0TuBZ*Yy~dF0RDV61;5jz&{Dy(OpHs;nw}yjWhz5)3A*FVD^Bc(=BSf zjSjO1OirWh7E3VdSgJk7o8Y6R*eKk7%b{f>!IS~6(KfJvY7CMmmTU>fATb(+(UzoxDxiNg8l1zmJ%TEOddsoqV<#Xt#mT|(?6ZuqGeGu(E(uC8jX3~S?ifpeZJE%TEN){!!He%!f^S9%+$WDlO9Uxe z-m-+EaWacO(WJ|1SxhQ^07IFBDFd$qBZy(DgPuH~3PgVR!AFdFAz+v1yCQ4YZ?t{dp>wbTV{wM-uxcuBlu%Z>WiY30)(l;l?k*qSUJ zlgEAZZFctLYQ`s4Sq`&jgk!< zlzecERk*1xz*6X26nt^yA#b2lt^i|u*YMdN2Q+apb zWG*>C8ElD_dOb9-> zUs6SXl}rvUIRH)pT4aCRQ*jEvWr3Z&xy%*kr{G2a)Eo@HY_)xr36P7_8ou;MrB1Ev1@6Z&U|4i093aSCRwZ5>J8y+1fEV z%)S&0(I_-ZY>Y8B)e3*9KJ|9cv10L~gM$iO!bq0NywH*(AMSJVUidHw4^LcQa;09R zAEQ1<0TOL%ZG?)EPz6u6-3NkUU|>|S-+lD0GB-+;MHqkW(^jhB12~YG0H;SjzRBd_ zvef=dUp>cK$CUzCplJ|U>B;P)!#NsUqPs1>&}xZCj;I80(8Ll9?xSlVy224hX_u?E zAZgGqJAcXQ3)PI#o|Q5+hO#e!4w+4^MX^x=a{xT1fZuAzim|d7t(Le?q2VX40zhx0 z!)WxCx9ETWc3R?SOZ(9o~hA@ zMhX0u!+$*xLC2y$cXm#y57ObRr8jo*N zz#Qz+w3S>%tSGdXK^JgHotKtZObw?Ca`li@LBauGfFYy-Ar$ZrF*c%0$Wi)0VP_pPSZ&4 zi_Bnj8bOw5SrVc$;JyOUl4p>G>O_1Ua&dpj>iQ7SaB|9ho^pD~JP7h7{2D4piWVm2 zB{UN>owtqXx#D%RH)xb-81%rHD3ytAz58HP1BXIwvC~68q&W2QHaeUt6^c+)@~5Yk zPnecyeH8MQ;B9cxsBKFn{6E5^m_UEY zeYB0k;XooEuBVZBVxxNunr9D}qE=!#KH?qp7pN!+qsjTYNnXepTN{R8zP z1^+r+P6_Vz(KY`|&7?=95^C_Qu|&*&j4Gi72P*H$BgBqDN1`Qn9zMa@`3f;gX3D_E z>g>gXzRGVSphVuP%NeacetTvNV}AgB=db*gKS~52oCkG@pCLc~l_FAb2v&a`Qvl4N zWcK@LTUj$YeNbjry00(js(^L|*8b<{-sfcf$-;+0fD|qoy%A*st%`_wKDaH=5epZgahAE9loUDFh!t3GO_| zyhd{=m&MWQOEh|WHM;|2!fJUTOCW;e5-f4lFCXD1D!~&q;Hdw)rqY3_AZqXDHqr^7X`(P3Xy|jaA{TpH_2ET5^A*)1A`K9!a#SGSI~x z&{BS<@|e3xngYDU;ItGQHR4H$(Uct+(dtbKp2^3OMVCf?;d<<~%SEgMB{)jKo036_ zi+B-WzeJSaZuCXzOEfrF1eJR0eWW(3kg3*K@>N3obH{G*b%N^Q3dtyuYWt%pAbZpH z&EIZqArX9VR?B}&&)jiZ;@cE(AAM7@?Fcds&dnIdp;4+)gGP&$7|&$e`B{1Qy-3x67g^dPsT z^VI|t-{zqGMJy8HIM5a$q7k^R)jEe_$&|xV37jkCWgk6mqer9V-2*7W59k;t-{y$> z6q*k%P4F|wjjkMVvBYZK!J?KTDZw`_i!%ER~1?pDUN=EN$WsvlPij& zuI5RIQtf}TmHA?{xGENC?}+UN+JA5IfRXl`)ZTZrs&bJm9y#FK6!0xa9D}_|tt1~3 z@sW%YxQ&J5K|!yNFAijLhf8yHwoxv61-`m&w^K!94|FIeYVE%cE;9u21V)!E*EWvk9OLBiREkKf@7MqMQs-EE}+I z>iTX;o#fp@HlsbCb1>)&HvpC3je$f1%{8TjF5T+!h#U-V$>Fyga2-87Eu@EtS zf+j8nhrLq1$x4krb5Tmc$WK~bePazGl#R6Fpqm`Zk!eE6zqZOvO9Uy7VDz>nS1dgi z#c#=@1C@G2E?V6*zWZ4+% zaIR;6^jT8Ritg!qk0v7;-S+j6fqh!p0n|p8z0_1LSK9TSO4T^m+`1g1rW=ou*mXvo zlR}eKNtG93Z!~V0ISZA#!}e-fj!u6c^joxoXb2YioIB0mK?@ zO139|(RXfv(n}%=pWJj!E9Bkio;@5izR%JNoR*nZ2Pyw_P32!I@Fss>QB9+cUw%G7 zfI?EMAS|>b$y7gPp8I&Pab472Xpyjxg1(> zM?0M;Q1(iuv?yM?jE85TkTga6(?_ncoNqbSEtl|u>iw@3kis<=QUgZna!<-ni7)?N zP!oIr%2_Gscu-q3YEy!{AMSGen?wo@lwMI0u*8$xVmoTqE8aH7UwKEgRMemE$p&wEsZzj81C0nU+qB}ZnZm6MVd-UwFNX#fBK delta 5477 zcmV-r6`JbLEbuO{V*!7Z+&Yu4vuA;ZX_y8I3JQt}YMG#@prE0tpy*K}+&hg2lA8xr+s>fHK1n?`wDv49Hc=b(s!)b{C0Gntr z7==#B^$WqO*(!lU(CD-r83W<9#9$Q8ovR-doSF4Y<;`Fej?sUjmV6FI;WjyRS|a^o z6{jT*-e9x}qgA+x7L63Ri58<(7>qjof>xs5srS0RDFyF3r!m^IFC?ollo#m>S_*$t zbvM=aOXa!uXqCA9<-wbo{@!*DC1<140M-#>2=y$8G}Kxk15(Z8lwaTBXQe#WB{Ub8bdLaUFXpogvDYm7!=?+vE%)E773b*|os)RQOCoVcNDU)}_Zn`lwC z&hX>|Wo3UPt5Hin0suHI=P?+3*ZA)VXCOZP<=Ji2Oz9}%mB zpQ1a7M^iktux@?odPgPrOo46g9J2E@LkW=A5<91HC_mPFjZRDa zx=G{}F_k1^n_N{1UAxZV#mKj5z4;IfdXpz;N*05WI3fEEdbA3Y(7WW|n=EePsXZE% z2lYIrl8@!UIhJ60GiCvRk-`tbEMu^^>m3f>>~DP*0M-E64mu|&*)%rMQ00y(2iTek zZ99i^A5rk^yOrRxRRVWIK&vGhDQey_#jJ5Mi#E}u%WIiUDt!P$*+iQMUIu?g5W`Rg zJp_Z2GZdUfu3JeS-?)q z?n495iI=RoQTsZrd>Vw3`~(5pb`JMLz+E5e?Cd&UGCo<&g_5`Sq8V-RI~+rB&X=K+#7!-{NN2Hmw)UW z&QyZ?G1$v=x(EXhW5@%XV*Xt3HBQmuHrbCY%sTLpHBqO|X6?pC8|~+_7oxg=T?8Y> zKy0GD9Y}tb zgV$sBKCj2)_rH6LCo%@RYzod#!Hoc@O|*Eo)%L4|F%VA6Zi<(AcCvauMTe$D<6QNu z^<@FKE^5+WCfk(r@Liw(wMv}SmXfn>?i}uVUlXx58}vrzoF{+U`-Q^Klwf29FE4tu z0AfnEHi9X=y;czaluBL-Zl-7#!YXflj`Hy-x!QTG?-lfwdRA+HQ(hFr@1+SJ5^93w zGR@x91ZivAIc)uH#zZu3qQ#+sI&W3)O7QzO%^8Sj)tvL%tjVcVG%chF0MI#&G{wI3 zVOk;JX4m`b0gQhXdv6rZoyT48u`A#@Rqph+QDS+wir;lqd1t7B>d0Ue?g|u9dTu9+ zY1=uRVqR<)nl4j5c!SaDSqVP+Vzaqa^BAmZAO~@~8FEj?XmCm^!GUaTKX@ENE*6qi zXqA`&0ESZGPsOL64>}brzV~KN1ukJEQ(<0c$&s_moVo)}fh*8fh%EPHcD=_ZJI%GCuDT=KUI7Wl}A>x#18(P@dhAy)jv7XawF_n53c z@fPjhUQ6sN;p;RvtCH+X11$60=lJv_kM+Y#OX z;P?gr@CJuh0HdS<0QkDR!r}M^kBos75AgKJfH8jtFRur9Jpvrz2#l@wZT~(3z{}wX zkB;YbuH+Xt{fbWMR{Oj8S-@Y%v0O0iq z@W>uu|Lr?`U0&gV0sQxv_5c8nj6uG|5g0Idd!t`^fWzx6Fkry)Ph7o@$0Kmg;AJ_& zP0@dn`zcyX1ymsqVpMJc4&I=ayrQDL)1~K_v6Ad*0zMBB$LRJHIJTWHh!utQ3?XE2 zNUfKaCrk~mGji2>rc3i!UiS|S;EClap=o*I5Aba{f_QoW$sa*JJ%W5 zmjk@L0qnni|NiY|fhWccXAF*TgtumTj{tvgJOVs&2J-Pt(~QC6W1b<#7%-Mi2;R;| zdM2dlXjR4fHo@YQEGC7=e%Rr<@8e*OfWfB7b5{e1d*M1 zu9BX<%TS}_n_S(dxSZab;_d`Gm4Dc4S#i8jrWhdO(9gZcslaX-xM9L7u@KHj&~tz9 z^@{OPd?=k!iV^Q-dQ-~XgDGX$iUNvJq9*I)zsY@3I@x=TM&@Xl6QVrewgS}kMx&6tmsTw?(vZP74kw=2LTX_!FCUGFM~!yAd5olYb1#Cm@nG#`Um zx?n15N~r2s9^^yt+a}umSg`1pi$DYJeI797*R>tuF40v0042DgZ1C1^G6sKQn{xAL z*E!=TkW=1!U9Z0^-l%ad^;_#NX~2|A*FhIx zoXqCdeSZir|KULwjAHOBbBavjob770m^Yc0f}Oj3pjM>dzYdpEg8N8t!>#3(V&#pU!) z@Bi`47=Yh^zVk=^$ZsWrGarmv(r3tzepf*=x%+yD zuKGtVMG0=U$t?s>c3!V6XCqH|B~!}q7ZuXa4c7NZ?g(cf#$fjubN7E`Q=llUB3X<1 zwhKq);WlMv(60CIbFzMC;fx6*g-=$`#LwMCo7s7t$IfYduhnjq7^x#OcfRh}2n`V7 z&sv?qw)oP@H@j65|Mns|GlZn)ht2HQT*2mIP;z$D(l`S{6iS*T0yKO@&qbruwdLT= zO6((a{Tx;QM+svfj1qrcFlyWcyT3*+z9%wg0d;Bj-ry$Y9(T0r0+4Bi_6^USE87ok z<*gEc*($efEw2-C+6blQzWSBL@sCKddOw$7LhZf5n;KyH^Zht?L<;6tKdLIZeQU7(@Iou|X=gK-Oje) zrmdpa7QSe%tmL+xUt0Pz{|w)qgfk|JGUuTn$aHN>FxfPiCs}6h3%ClSm+flvIhsOZ zm6$i>eq*pWmkobon`G}J)sSKP+`!Htb>>Vsd>+C{PuWPZr|oi-MeK)A$4ow+@MC*+ zwJt+TvFD8WThH^u!T_qGHzlDbpVk+J*M3^bJ!#4HUN=f#dnC!J!ax@nKui9e!eeeG zX^7@72B)Q%sS!^~Og8VpNKVgE@R58hS#)mXXRgPtbh&?sb)W=CDR@<~kzx^d0_-ml zCAjZHR{E4IZVG}*z4bOyTb0RF+m!QFLj1>u-4MzImBSS}t3+4YA5{Tkuyx=3`PLQ^ z!I^hjE-iD%Yl+W8#9be%l3hcP@n+qOu^$3XWt=8k>HvO&KdK9v(6~7P&!D@HZ^7Z6m9Jbe;S1hM$S{?s|hGR z&q3RZSR~n}+qy-FXav4#wQgdzWXfTw1Re|JW!HZPocn-Q%Zmq4g6~bkIQg(2D#qnBQB;?tUEf?Qgll2S<9@NaWj$TdA) z@I$W2%lE@p$wyG~vC?}Eg;sPp02~fbnKExj0Kp#N@Op&h^$3p)Ks-Ib%i9|)uLpSf zet?(PH~8}H058iCzP%i>(jEbp!x6rH&qRNaZ!ZVfe_1kxXgRE-YW zudhdVU<{7%2Fvj;>`+l6pA3;L+hE(&4b75T$-9|sMteRt(PGHl091lk1`=7ySW%>eA1e9y{7O!j`{h;l-1|a(xwiT5 zoY{1CHOwapikXP<7ii*AaI-6v?xI!LjJW@ zZd#&|Vjr!Z=g#9M<-+C57;)|bdZT~37LiL%S59UN7Loh%T2_2|F8i+NxUYLL=-%Z` zCWh<^DXMgBKMzXv&Mai5V>Ff3O@7)G4k!UbyP{;G5z|ArX9G0@Ug%JwmKsc!MhCUD zyQJsN^iH~NukLqOwfhc8#C*O%unL2f6>5kL$FBF8LO*y@&sR%URR$yF+@CINw9H$bDQ70d1f#0Y zCiY^RNqCddvDTuAh)RT5qRrEWhL)R0!y-91gvt>|iWID9qkVI%!MTD@ z{ae}7UfxrJcV5e`-J?Sf))jL4y@mi|c~ASU`0zedc;Q3%ID@?HoMHhr6bRdC6;Ef8 z623#9Ay(5*4k#DZHSkWwW2oBDg@-{qQ> zPKh=ItcoV+R!i}rBEc19k){6?CGdP!?-;EbgHiJs%-(A`{!(5jNwS%VOS8L)c6A9x zow$J5D7kV;?QMT@E4hN1=*e!SC6_Mwhl7pa%#d3!=ZE(FE@GAFD8HptsrAXe32c-> zkdm+QUr9vilvCG-==yYOO#I|QR-0&--BHrYKwM&P6 b?@dm9-lH1)kxThsy5jYawUCA0lW`T^XLYR{ diff --git a/pngwutil.c b/pngwutil.c index 4e9322908..b5e2967d7 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -469,6 +469,56 @@ png_zlib_compress_init(png_structrp png_ptr, png_zlib_compressp pz) */ #define png_ptr png_voidcast(png_const_structrp, pz->zs.opaque) +#if PNG_RELEASE_BUILD +# define png_zlib_compress_vaidate(pz) ((void)0) +#else /* !RELEASE_BUILD */ +static void +png_zlib_compress_validate(png_zlib_compressp pz, int in_use) +{ + const uInt o_size = sizeof pz->list->output; + + affirm(pz->end != NULL && (in_use || (pz->zs.next_in == NULL && + pz->zs.avail_in == 0U && *pz->end == NULL))); + + if (pz->overflow == 0U && pz->len == 0U && pz->start == 0U) /* empty */ + { + affirm((pz->end == &pz->list->next && pz->zs.next_out == pz->list->output + && pz->zs.avail_out == o_size) || + (pz->end == &pz->list && pz->zs.next_out == NULL + && pz->zs.avail_out == 0U)); + } + + else /* not empty */ + { + png_compression_bufferp *ep = &pz->list, list; + png_uint_32 o, l; + + affirm(*ep != NULL && pz->zs.next_out != NULL); + + /* Check the list length: */ + o = pz->overflow; + l = pz->len; + affirm((l & 0x80000000U) == 0U && (o & 0x80000000U) == 0U); + + do + { + list = *ep; + l -= o_size; + if (l & 0x80000000U) --o, l &= 0x7FFFFFFFU; + ep = &list->next; + } + while (ep != pz->end); + + l += pz->start; + l += pz->zs.avail_out; + if (l & 0x80000000U) ++o, l &= 0x7FFFFFFFU; + + affirm(o == 0U && l == 0U && pz->zs.next_out >= list->output && + pz->zs.next_out + pz->zs.avail_out == list->output + o_size); + } +} +#endif /* !RELEASE_BUILD */ + /* Destroy one zlib compress structure. */ static void png_zlib_compress_destroy(png_zlib_compressp pz, int check) @@ -479,7 +529,12 @@ png_zlib_compress_destroy(png_zlib_compressp pz, int check) if (png_ptr != NULL) { if (pz->zs.state != NULL) + { + if (check) + png_zlib_compress_validate(pz, 0/*in_use*/); + png_deflateEnd(png_ptr, &pz->zs, check); + } pz->end = &pz->list; /* safety */ png_free_compression_buffer(png_ptr, &pz->list); @@ -494,6 +549,8 @@ png_zlib_compress_avail_out(png_zlib_compressp pz) { uInt avail_out = pz->zs.avail_out; + png_zlib_compress_validate(pz, 1/*in_use*/); + if (avail_out == 0U) { png_compression_bufferp next; @@ -2257,6 +2314,7 @@ png_write_IDAT(png_structrp png_ptr, int flush) * pointer is NULL means that the end of the list can be easily detected. */ affirm(ps != NULL && ps->s.end != NULL && *ps->s.end == NULL); + png_zlib_compress_validate(&png_ptr->zlib_state->s, 0/*in_use*/); /* Write IDAT chunks while either 'flush' is true or there are at * least png_ptr->IDAT_size bytes available to be written. @@ -2277,7 +2335,7 @@ png_write_IDAT(png_structrp png_ptr, int flush) if (!flush) return; - if (avail == 0) + if (avail == 0U) break; len = avail; @@ -2309,8 +2367,9 @@ png_write_IDAT(png_structrp png_ptr, int flush) } else /* not end of list */ - debug(ps->s.zs.next_out < next->output || - ps->s.zs.next_out > next->output + sizeof next->output); + debug((ps->s.zs.next_out < next->output || + ps->s.zs.next_out > next->output + sizeof next->output) && + (ps->s.overflow > 0 || ps->s.len >= sizeof next->output)); /* First, if this is the very first IDAT (PNG_HAVE_IDAT not set) * optimize the CINFO field: @@ -2375,6 +2434,7 @@ png_write_IDAT(png_structrp png_ptr, int flush) affirm(ps->s.overflow > 0U); --ps->s.overflow; ps->s.len += 0x80000000U - written; + UNTESTED } } while (len > 0U); @@ -2424,6 +2484,7 @@ png_compress_IDAT_data(png_const_structrp png_ptr, png_zlib_statep ps, (ret == Z_STREAM_END) == (flush == Z_FINISH)); pz->zs.next_in = NULL; pz->zs.avail_in = 0U; /* safety */ + png_zlib_compress_validate(pz, 0/*in_use*/); return ret; } @@ -2983,9 +3044,10 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i) png_zlib_compressp pz = &ps->filter[i]; affirm(pz->zs.opaque != NULL); + png_zlib_compress_validate(pz, 0/*in_use*/); /* First merge the buffer lists. */ - if (pz->overflow || pz->len > 0U) + if (pz->overflow > 0U || pz->len > 0U) { affirm(pz->list != NULL); debug(ps->s.end != NULL && *ps->s.end == NULL); @@ -2995,8 +3057,9 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i) * the main z_stream, if pz->zs.next_out still points into this buffer the * pointer must be updated to point to the old buffer. */ - if (pz->start > 0U) + if (ps->s.zs.avail_out > 0U) { + affirm(ps->s.zs.avail_out + pz->start == sizeof ps->s.list->output); /* Copy everything after pz->start into the old buffer. */ memcpy(ps->s.zs.next_out, pz->list->output + pz->start, ps->s.zs.avail_out); @@ -3022,6 +3085,8 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i) { debug(pz->overflow == 0U && pz->len + pz->start < (sizeof pz->list->output) && + pz->zs.next_out + pz->zs.avail_out == + pz->list->output + (sizeof pz->list->output) && ps->s.zs.avail_out > pz->zs.avail_out); pz->zs.next_out = ps->s.zs.next_out + ps->s.zs.avail_out - pz->zs.avail_out; @@ -3030,6 +3095,8 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i) else { + affirm(pz->start == 0U); + /* Nothing to copy, the whole new list is appended to the existing one. */ *ps->s.end = pz->list; @@ -3059,11 +3126,11 @@ png_zlib_filter_revert(png_structrp png_ptr, png_zlib_statep ps, png_byte i) { z_stream zs = ps->s.zs; + ps->s.zs = pz->zs; + png_zlib_compress_validate(&ps->s, 0/*in_use*/); zs.next_in = zs.next_out = NULL; zs.avail_in = zs.avail_out = 0U; zs.msg = PNGZ_MSG_CAST("invalid"); - - ps->s.zs = pz->zs; pz->zs = zs; } } @@ -3137,6 +3204,113 @@ select_filter_methodically_init(png_structrp png_ptr, } } +static int +select_filter_methodically_better(png_structrp png_ptr, png_zlib_compressp pz, + png_uint_32p op/*high 32 bits*/, png_uint_32p lp/*low 31 bits*/, + Bytef *scratch_out, uInt avail_out, int flush) + /* Called at the end of a row for each filter being tested to work out if + * this filter is apparently producing better results than {*op,*lp}, which + * is preset to a number larger than any possible 63-bit value and then set, + * here, as required to {overflow,len} from a selected filter. + */ +{ + /* The pre-check here is that the data already produced by the compression + * engine does not exceed the best count found so far: + */ + png_uint_32 o = pz->overflow, l = pz->len; + + png_zlib_compress_validate(pz, 0/*in_use*/); + + if (o < *op || (o == *op && l < *lp)) + { + /* But if the stream hasn't been flushed this proves nothing; test the + * pending output by using an appropriate flush: + */ + if (flush == Z_NO_FLUSH) + { + int ret; + z_stream zs; + + ret = deflateCopy(&zs, &pz->zs); + + if (ret == Z_OK) + { + zs.next_in = NULL; + zs.avail_in = 0U; + + /* Extract all the output from zlib by doing dummy deflates. Note + * that all the flush possibilites give approximately the same + * result but PARTIAL, SYNC and FULL seem to be mildly better + * probably because they avoid the rounding and block overhead. + * + * Z_PARTIAL_FLUSH 1 + * Z_SYNC_FLUSH 2 + * Z_FULL_FLUSH 3 + * Z_FINISH 4 + * Z_BLOCK 5 + */ + flush = Z_PARTIAL_FLUSH; + + do + { + if (l & 0x80000000U) + ++o, l &= 0x7FFFFFFFU; + + zs.next_out = scratch_out; + zs.avail_out = avail_out; + l += avail_out; + + ret = deflate(&zs, flush); + } while (ret == Z_OK && zs.avail_out == 0U); + + if (ret == (flush == Z_FINISH ? Z_STREAM_END : Z_OK)) + { + /* This cannot underflow because the check above is performed + * before adding 'avail_out' to l: + */ + l -= zs.avail_out; + (void)deflateEnd(&zs); + png_zlib_compress_validate(pz, 0/*in_use*/); + + if (l & 0x80000000U) + ++o, l &= 0x7FFFFFFFU; + + if (o < *op || (o == *op && l < *lp)) + { + *op = o; + *lp = l; + return 1; + } + + /* No errors but the result was longer (this can't be the first + * filter.) + */ + return 0; + } + + else /* problem in deflate */ + (void)deflateEnd(&zs); + } + + /* We arrive here if there was an error somewhere inside zlib. */ + png_zstream_error(&zs, ret); + png_warning(png_ptr, zs.msg); + } + + else /* flush already performed */ + { + *op = o; + *lp = l; + return 1; + } + } + + /* This is the failure case, however if this is the first filter to be tested + * return success anyway, without resetting {op,lp}: + */ + return *op == 0xFFFFFFFFU && *lp == 0xFFFFFFFFU; +} + static void select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row, png_bytep prev_pixels, png_const_bytep unfiltered_row, @@ -3167,8 +3341,7 @@ select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row, { if (png_zlib_filter_compress(png_ptr, ps, filter, filter == PNG_FILTER_VALUE_NONE ? - unfiltered_row : test_buffers[filter-1], - row_bytes, flush)) + unfiltered_row : test_buffers[filter-1], row_bytes, flush)) ok_filter = filter; else /* remove this filter from the test list: */ @@ -3188,15 +3361,10 @@ select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row, ok_filter = PNG_FILTER_VALUE_LAST; for (filter=0U; filter < PNG_FILTER_VALUE_LAST; ++filter) - if ((filters_to_try & PNG_FILTER_MASK(filter)) != 0U) - if (ps->filter[filter].overflow < o || - (ps->filter[filter].overflow == o && - ps->filter[filter].len < l)) - { - ok_filter = filter; - o = ps->filter[filter].overflow; - l = ps->filter[filter].len; - } + if ((filters_to_try & PNG_FILTER_MASK(filter)) != 0U && + select_filter_methodically_better(png_ptr, &ps->filter[filter], + &o, &l, test_buffers[0], sizeof test_buffers, flush)) + ok_filter = filter; } /* Keep going if there is more than one filter left, otherwise, if there