c# gzip fiddler gzipstream

¿Por qué mi C#gzip produce un archivo más grande que Fiddler o PHP?



gzipstream (5)

Si brindo este texto:

Hola Mundo

a través de C # usando este código:

Stream stream = new MemoryStream(Encoding.Default.GetBytes("Hello World")); var compressedMemoryStream = new MemoryStream(); using (var gzipStream = new GZipStream(compressedMemoryStream, CompressionMode.Compress)) { stream.CopyTo(gzipStream); gzipStream.Close(); }

la secuencia resultante tiene 133 bytes de longitud

Al ejecutar la misma cadena a través de Fiddler''s Utilities.GzipCompress o esta página PHP, el resultado es de solo 31 bytes de longitud.

En ambos casos, la entrada es de 11 bytes, por lo que me imagino que el resultado de PHP es correcto, pero obviamente esto significa que no puedo descomprimir el zip de PHP desde .NET o viceversa. ¿Por qué la salida de .NET es mucho más grande?

En realidad, resulta que si bien el resultado de PHP y Fiddler son de la misma longitud que no son lo mismo. Puedo descomprimir la versión de PHP en .NET, pero no la versión de Fiddler. La página PHP descomprime los tres, por lo que parece que puede haber una incompatibilidad entre las implementaciones de gzip de Fiddler y .NET.

Según lo solicitado, he subido las tres salidas a Dropbox here .

Y estos son los hexdumps crudos de esos archivos (no estoy seguro si realmente tienen algún uso como este, pero creo que muestra que la diferencia entre el violín y la versión de PHP está en el encabezado, en lugar de los datos comprimidos en sí):

Violinista:

0000-0010: 1f 8b 08 00-c2 e6 ff 4f-00 ff f3 48-cd c9 c9 57 .......O ...H...W 0000-001f: 08 cf 2f ca-49 01 00 56-b1 17 4a 0b-00 00 00 ../.I..V ..J....

PHP:

0000-0010: 1f 8b 08 00-00 00 00 00-00 03 f3 48-cd c9 c9 57 ........ ...H...W 0000-001f: 08 cf 2f ca-49 01 00 56-b1 17 4a 0b-00 00 00 ../.I..V ..J....

DO#:

0000-0010: 1f 8b 08 00-00 00 00 00-04 00 ec bd-07 60 1c 49 ........ .....`.I 0000-0020: 96 25 26 2f-6d ca 7b 7f-4a f5 4a d7-e0 74 a1 08 .%&/m.{. J.J..t.. 0000-0030: 80 60 13 24-d8 90 40 10-ec c1 88 cd-e6 92 ec 1d .`.$..@. ........ 0000-0040: 69 47 23 29-ab 2a 81 ca-65 56 65 5d-66 16 40 cc iG#).*.. eVe]f.@. 0000-0050: ed 9d bc f7-de 7b ef bd-f7 de 7b ef-bd f7 ba 3b .....{.. ..{....; 0000-0060: 9d 4e 27 f7-df ff 3f 5c-66 64 01 6c-f6 ce 4a da .N''...?/ fd.l..J. 0000-0070: c9 9e 21 80-aa c8 1f 3f-7e 7c 1f 3f-22 be 9d 97 ..!....? ~|.?"... 0000-0080: 65 95 7e b7-aa cb d9 ff-13 00 00 ff-ff 56 b1 17 e.~..... .....V.. 0000-0085: 4a 0b 00 00-00


Las herramientas de compresión modernas generalmente usarán más de una estrategia de compresión. Con Winzip y WinRAR, etc., normalmente obtendrá opciones como:

  • Compresión máxima
  • Lo más rápido
  • Normal

Si hicieras lo mismo, probablemente serías capaz de comprimir el archivo más.


No importa qué contenido alimente en GZipStream, obtiene la misma sobrecarga. GZipStream parece idéntico para los primeros 108 bytes

1f 8b 08 00 00 00 00 00 04 00 ec bd 07 60 1c 49 96 25 26 2f 6d ca 7b 7f 4a f5 4a d7 e0 74 a1 08 80 60 13 24 d8 90 40 10 ec c1 88 cd e6 92 ec 1d 69 47 23 29 ab 2a 81 ca 65 56 65 5d 66 16 40 cc ed 9d bc f7 de 7b ef bd f7 de 7b ef bd f7 ba 3b 9d 4e 27 f7 df ff 3f 5c 66 64 01 6c f6 ce 4a da c9 9e 21 80 aa c8 1f 3f 7e 7c 1f 3f 22 >>>

Hasta 1f 8b 08 00 00 00 00 00 04 00 ajusta a la definición estándar (ttp: //www.faqs.org/rfcs/rfc1952.html). El resto de la sección fija ha sido explicado por @ mark-adler en ¿Por qué BCL GZipStream (con StreamReader) no detecta confiablemente los errores de datos con CRC32?


Patentes parciales:

La razón por la que el nivel de compresión no es tan bueno como con otras aplicaciones es que los algoritmos de compresión más eficientes del mercado están protegidos por patentes. .net por otro lado utiliza uno no patentado.

y

Bueno, la explicación que obtuve (de alguien en MS), cuando le pregunté lo mismo, fue que tenía que ver con que Microsoft no podía usar el algoritmo de GZip sin modificarlo; debido a problemas de patentes / licencias.

http://social.msdn.microsoft.com/Forums/fr-FR/c5f0b53c-a2d5-4407-b43b-9da8d39c01df/why-do-gzipstream-compression-ratio-so-bad?forum=netfxbcl

Inicial, sospeché la implementación de gzip de Microsoft; Sabía que implementaron el algoritmo Deflate, que no es el más efectivo, pero está libre de patentes.

http://challenge-me.ws/post/2010/11/05/Do-Not-Take-Microsofts-Code-for-Granted.aspx


GZipStream agrega un encabezado de 10 bytes y un pie de página de 8 bytes a los datos comprimidos como se describe en las especificaciones RFC 1952 . Esto da un resultado de 133 bytes de longitud.

La página PHP a la que vinculó también agrega el mismo encabezado / pie de página de 18 bytes si se le solicita ( GZIP-compatible encoding? ). Si usa eso, da un resultado de 31 bytes de largo.

Sin el encabezado / pie de página la diferencia entre ellos es de 125 contra 13 bytes.


Prefacio: los usuarios de .NET no deberían usar las clases GZipStream o DeflateStream proporcionadas por Microsoft bajo ninguna circunstancia, a menos que Microsoft las reemplace por completo con algo que funcione. Use la biblioteca DotNetZip en su lugar.

Actualización al Prefacio: .NET Framework 4.5 y posterior han solucionado el problema de compresión, y GZipStream y DeflateStream usan zlib en esas versiones. No sé si se ha solucionado el problema de CRC a que se hace referencia a continuación.

Otra actualización: ¡ El problema de CRC no solo no está solucionado, sino que Microsoft decidió que no lo arreglarían !

Este es uno de varios errores en GZipStream. Ningún compresor gzip que se precie deberá producir 133 bytes de salida a partir de 11 bytes de entrada. Ver mis comentarios en ¿Por qué BCL GZipStream (con StreamReader) no detecta confiablemente los errores de datos con CRC32? .

Lo que está ocurriendo internamente es que GZipStream no está usando los métodos estáticos o almacenados, los cuales producirían datos comprimidos del mismo tamaño que los datos de entrada (sobre los cuales se agregarían 18 bytes de encabezado y tráiler de gzip). En su lugar, utiliza el método dinámico, que crea un encabezado de descriptor de código muy grande para una cantidad muy pequeña de códigos. Es simplemente un error / muy mala implementación.

Actualizar:

Con los volcados hexadecimales, puedo proporcionar algunos análisis. En primer lugar, tanto el Fiddler como la salida php son correctos y adecuados. La única diferencia entre ellos es en el encabezado de gzip, en particular la marca de tiempo configurada en Fiddler pero no en php, y el sistema operativo de origen configurado en php pero no en Fiddler. Para ambos, los 13 bytes de datos comprimidos son idénticos, y se pueden representar como (usando mi programa infgen para desmontar flujos desinflados):

last static literal ''Hello World end

que es exactamente como debería ser Un solo bloque estático, que no requiere descriptores de código, y simplemente codifica todos los bytes como literales. (Sin coincidencias de cadenas anteriores con longitudes y distancias).

La salida de GZipStream por otro lado es un desastre horrible de varias maneras. La información comprimida es:

dynamic code 3 5 code 4 5 code 5 4 code 6 4 code 7 4 code 8 3 code 9 3 code 10 4 code 11 4 code 12 4 code 13 4 code 14 3 code 16 3 litlen 0 14 litlen 1 14 litlen 2 14 litlen 3 14 litlen 4 14 litlen 5 14 litlen 6 14 litlen 7 14 litlen 8 14 litlen 9 12 litlen 10 6 litlen 11 14 litlen 12 14 litlen 13 14 litlen 14 14 litlen 15 14 litlen 16 14 litlen 17 14 litlen 18 14 litlen 19 14 litlen 20 14 litlen 21 14 litlen 22 14 litlen 23 14 litlen 24 14 litlen 25 14 litlen 26 14 litlen 27 14 litlen 28 14 litlen 29 14 litlen 30 13 litlen 31 14 litlen 32 6 litlen 33 14 litlen 34 10 litlen 35 12 litlen 36 14 litlen 37 14 litlen 38 13 litlen 39 10 litlen 40 8 litlen 41 9 litlen 42 11 litlen 43 10 litlen 44 7 litlen 45 8 litlen 46 7 litlen 47 9 litlen 48 8 litlen 49 8 litlen 50 8 litlen 51 9 litlen 52 8 litlen 53 9 litlen 54 10 litlen 55 9 litlen 56 8 litlen 57 9 litlen 58 9 litlen 59 8 litlen 60 9 litlen 61 10 litlen 62 8 litlen 63 14 litlen 64 14 litlen 65 8 litlen 66 9 litlen 67 8 litlen 68 9 litlen 69 8 litlen 70 9 litlen 71 10 litlen 72 11 litlen 73 8 litlen 74 11 litlen 75 14 litlen 76 9 litlen 77 10 litlen 78 9 litlen 79 10 litlen 80 9 litlen 81 12 litlen 82 9 litlen 83 9 litlen 84 9 litlen 85 10 litlen 86 12 litlen 87 11 litlen 88 14 litlen 89 14 litlen 90 12 litlen 91 11 litlen 92 14 litlen 93 11 litlen 94 14 litlen 95 14 litlen 96 14 litlen 97 6 litlen 98 7 litlen 99 7 litlen 100 7 litlen 101 6 litlen 102 8 litlen 103 8 litlen 104 7 litlen 105 6 litlen 106 12 litlen 107 9 litlen 108 6 litlen 109 7 litlen 110 7 litlen 111 6 litlen 112 7 litlen 113 13 litlen 114 6 litlen 115 6 litlen 116 6 litlen 117 7 litlen 118 8 litlen 119 8 litlen 120 9 litlen 121 8 litlen 122 11 litlen 123 13 litlen 124 12 litlen 125 13 litlen 126 13 litlen 127 14 litlen 128 14 litlen 129 14 litlen 130 14 litlen 131 14 litlen 132 14 litlen 133 14 litlen 134 14 litlen 135 14 litlen 136 14 litlen 137 14 litlen 138 14 litlen 139 14 litlen 140 14 litlen 141 14 litlen 142 14 litlen 143 14 litlen 144 14 litlen 145 14 litlen 146 14 litlen 147 14 litlen 148 14 litlen 149 14 litlen 150 14 litlen 151 14 litlen 152 14 litlen 153 14 litlen 154 14 litlen 155 14 litlen 156 14 litlen 157 14 litlen 158 14 litlen 159 14 litlen 160 14 litlen 161 14 litlen 162 14 litlen 163 14 litlen 164 14 litlen 165 14 litlen 166 14 litlen 167 14 litlen 168 14 litlen 169 14 litlen 170 14 litlen 171 14 litlen 172 14 litlen 173 14 litlen 174 14 litlen 175 14 litlen 176 14 litlen 177 14 litlen 178 14 litlen 179 14 litlen 180 14 litlen 181 14 litlen 182 14 litlen 183 14 litlen 184 14 litlen 185 14 litlen 186 14 litlen 187 14 litlen 188 14 litlen 189 14 litlen 190 14 litlen 191 14 litlen 192 14 litlen 193 14 litlen 194 14 litlen 195 14 litlen 196 14 litlen 197 14 litlen 198 14 litlen 199 14 litlen 200 14 litlen 201 14 litlen 202 14 litlen 203 14 litlen 204 14 litlen 205 14 litlen 206 14 litlen 207 14 litlen 208 14 litlen 209 14 litlen 210 14 litlen 211 14 litlen 212 14 litlen 213 14 litlen 214 14 litlen 215 14 litlen 216 14 litlen 217 14 litlen 218 14 litlen 219 14 litlen 220 14 litlen 221 14 litlen 222 14 litlen 223 14 litlen 224 14 litlen 225 14 litlen 226 14 litlen 227 14 litlen 228 14 litlen 229 14 litlen 230 14 litlen 231 14 litlen 232 14 litlen 233 14 litlen 234 14 litlen 235 14 litlen 236 14 litlen 237 14 litlen 238 14 litlen 239 14 litlen 240 14 litlen 241 14 litlen 242 14 litlen 243 13 litlen 244 13 litlen 245 13 litlen 246 14 litlen 247 13 litlen 248 14 litlen 249 13 litlen 250 14 litlen 251 13 litlen 252 14 litlen 253 14 litlen 254 14 litlen 255 14 litlen 256 14 litlen 257 4 litlen 258 3 litlen 259 4 litlen 260 4 litlen 261 4 litlen 262 5 litlen 263 5 litlen 264 5 litlen 265 5 litlen 266 5 litlen 267 6 litlen 268 6 litlen 269 5 litlen 270 6 litlen 271 7 litlen 272 8 litlen 273 8 litlen 274 9 litlen 275 10 litlen 276 9 litlen 277 10 litlen 278 12 litlen 279 11 litlen 280 12 litlen 281 14 litlen 282 14 litlen 283 14 litlen 284 12 litlen 285 11 dist 0 6 dist 1 10 dist 2 11 dist 3 11 dist 4 9 dist 5 8 dist 6 8 dist 7 8 dist 8 7 dist 9 7 dist 10 5 dist 11 6 dist 12 4 dist 13 5 dist 14 4 dist 15 5 dist 16 4 dist 17 5 dist 18 4 dist 19 4 dist 20 4 dist 21 4 dist 22 4 dist 23 4 dist 24 4 dist 25 5 dist 26 4 dist 27 5 dist 28 5 dist 29 5 literal ''Hello World end ! last stored end

Entonces, ¿qué es todo eso? Los datos reales son solo la línea cerca del final "literal" Hello World ", que simplemente codifica cada byte de la entrada. Lo que le precede es una descripción de un conjunto de códigos de Huffman para literales, longitudes y distancias. Aquí están las cosas malas con eso:

  • En primer lugar, no debería ser dinámico. La descripción del conjunto de códigos toma alrededor de 100 bytes. Esta es precisamente la razón por la que el formato desinflado proporciona un conjunto predefinido de códigos utilizados en bloques estáticos. El compresor debería seleccionar un bloque estático en este caso (que es lo que están haciendo php y Fiddler).
  • En segundo lugar, se define cada código posible, ¡aunque la gran mayoría nunca se usa! Cuando se utiliza un bloque dinámico, un compresor adecuado solo definirá códigos para literales, longitudes y distancias realmente utilizadas en ese bloque. En este caso, no se utilizan longitudes ni distancias, y solo se usaron ocho literales diferentes (H, e, l, o, espacio, w, r y d). En cambio, procede a definir 256 códigos literales, 29 códigos de longitud y 30 códigos de distancia. Supongo que algunos experimentos mostrarán que el encabezado dinámico de GZipStream es siempre el mismo, en cuyo caso ni siquiera es dinámico, ¡que es el punto!
  • En tercer lugar, arroja un bloque almacenado vacío innecesario al final. El primer bloque debería haber sido marcado como el último bloque.

Todo esto apunta al simple hecho de que quien escribió este código GZipStream fue, por decirlo de la manera más educada que pude, sin ningún conocimiento del formato desinflado o compresión en general. Eligieron producir solo bloques dinámicos (excepto un bloque estático vacío al final), producir solo el mismo encabezado dinámico cada vez (creo), derrotando el propósito de los bloques dinámicos, y no molestarse en averiguar si el actual block es el último, que requiere poner un bloque vacío para marcar el final.

Como se señaló en otra parte, esos no son los únicos problemas con GZipStream. Ni siquiera puede usar correctamente el CRC-32 como pretende detectar corrientes corruptas.

Lo realmente desconcertante no es por qué Microsoft asignó a alguien incompetente para escribir un compresor gzip y un descompresor, ¡sino más bien por qué le asignaron a alguien para que lo escriba! Existe un código de libre acceso, zlib , que tiene una licencia extremadamente liberal que permite el uso comercial sin atribución. Este código se ha implementado ampliamente durante casi dos décadas y hace todo lo que se supone que debe hacer de manera correcta y eficiente. La mayoría de todo lo demás usa zlib, incluido php y también sospecho Fiddler.