2012-01-17 23 views
13

Estoy usando zlib para comprimir una secuencia de datos de texto. Los datos de texto vienen en fragmentos, y para cada fragmento, se llama a deflate(), con el conjunto de color a Z_NO_FLUSH. Una vez que se han recuperado todos los fragmentos, se llama al deflate() con el juego de color a Z_FINISH.zlib, deflate: ¿Cuánta memoria asignar?

Naturalmente, deflate() no produce salida comprimida en cada llamada. Internamente acumula datos para lograr una alta tasa de compresión. ¡Y eso está bien! Cada vez que deflate() produce una salida comprimida, esa salida se agrega a un campo de base de datos, un proceso lento.

Sin embargo, una vez que deflate() produce datos comprimidos, es posible que esos datos no quepan en el búfer de salida proporcionado, deflate_out. Por lo tanto, se requieren varias llamadas al deflate(). Y eso es lo que quiero evitar:

¿Hay una manera de hacer deflate_out siempre lo suficientemente grande como para que deflate() puede almacenar todos los datos comprimidos en el mismo, cada momento que decida producir una salida?

Notas:

  • El tamaño total de los datos sin comprimir es no conocido de antemano. Como se mencionó anteriormente, los datos descomprimidos vienen en fragmentos, y los datos comprimidos se anexan a un campo de base de datos, también en fragmentos.

  • En el archivo de inclusión zconf.h he encontrado el siguiente comentario. ¿Es eso quizás lo que estoy buscando? Es decir. es (1 << (windowBits+2)) + (1 << (memLevel+9)) el tamaño máximo en bytes de datos comprimidos que deflate() puede producir?

    /* The memory requirements for deflate are (in bytes): 
          (1 << (windowBits+2)) + (1 << (memLevel+9)) 
    that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) 
    plus a few kilobytes for small objects. For example, if you want to reduce 
    the default memory requirements from 256K to 128K, compile with 
        make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" 
    Of course this will generally degrade compression (there's no free lunch). 
    
        The memory requirements for inflate are (in bytes) 1 << windowBits 
    that is, 32K for windowBits=15 (default value) plus a few kilobytes 
    for small objects. 
    */ 
    
+0

Consulte http://stackoverflow.com/questions/4936255/zlib-how-to-dimension-avail-out – nos

+2

@nos: Esto solo es útil, si se conoce el tamaño de la entrada. –

+0

Leí el comentario en 'zconf.h' para que sea el requisito de memoria para la compresión, no el tamaño del buffer de salida. Dicho esto, parece lógico, que un límite superior para el búfer de salida sea el total de requisitos de memoria (128K + 128K + "algunos kilobytes" en el ejemplo anterior) + longitud del encabezado (40 bytes). –

Respuesta

5

deflateBound() es útil solo si realiza toda la compresión en un solo paso, o si fuerza el desinflado para comprimir todos los datos de entrada actualmente disponibles y emitir datos comprimidos para toda esa entrada. Se podría hacer eso con un parámetro ras tales como Z_BLOCK, Z_PARTIAL_FLUSH, etc.

Si desea utilizar Z_NO_FLUSH, entonces se vuelve mucho más difícil, así como ineficiente para intentar predecir la mayor cantidad de desinflado de salida() podría emitir en la próxima llamada. No sabe qué cantidad de la entrada se consumió en el momento en que se emitió la última ráfaga de datos comprimidos, por lo que no debe asumir casi nada, con el tamaño del búfer aumentando innecesariamente. Sin embargo, si intenta estimar la salida máxima, estará realizando una gran cantidad de mallocs o reasignaciones innecesarios sin una buena razón, que es ineficiente.

No tiene sentido evitar llamar a deflate() para obtener más resultados.Si simplemente realiza un bucle en deflate() hasta que no tenga más salida para usted, entonces puede usar un buffer de salida fijo malloced una vez. Así es como se diseñó la interfaz desinflar() e inflar(). Puede mirar http://zlib.net/zlib_how.html para obtener un ejemplo bien documentado de cómo usar la interfaz.

Por cierto, hay una función deflatePending() en la última versión de zlib (1.2.6) que le permite saber cuánto espera deflate() esperar para entregar.

+0

¡Muchas gracias por esta respuesta detallada! Para predecir el buffer de salida requerido para la próxima llamada a 'deflate()', consideré agregar el tamaño reportado por 'deflatePending()' y el valor devuelto por 'deflateBound()'. Eso es similar a la sugerencia de @EugenRieck. Sin embargo, tal como lo entiendo, esta no es una buena idea ya que 'deflateBound()' está documentado para que funcione solo cuando se pasa el tamaño de la entrada completa que se comprimirá. Es decir. 'deflateBound()' no está documentado para trabajar en fragmentos de entrada. – feklee

+1

deflateBound() podría funcionar para fragmentos de entrada, pero solo si todas las entradas anteriores se hubieran comprimido y emitido. Esto solo se puede garantizar utilizando una opción de descarga que no sea Z_NO_FLUSH y consumiendo toda la salida de las llamadas anteriores. En este caso, deflatePending() sería útil para cuando se usa Z_BLOCK o Z_PARTIAL_FLUSH, ya que pueden dejar algunos bits atrás. Al usar Z_NO_FLUSH, deflateBound() + deflatePending() faltaría la tercera pieza, que es el tamaño de la entrada consumida en las llamadas de deflate() anteriores, pero aún no está comprimida ni emitida. –

2

mientras mira a las fuentes para una pista, que cayeron sobre

/* ========================================================================= 
* Flush as much pending output as possible. All deflate() output goes 
* through this function so some applications may wish to modify it 
* to avoid allocating a large strm->next_out buffer and copying into it. 
* (See also read_buf()). 
*/ 
local void flush_pending(strm) 
    z_streamp strm; 
{ 
    unsigned len = strm->state->pending; 
... 

rastrear el uso de flush_pending vacío() a lo largo de desinflado() muestra que un límite superior a la necesaria El búfer de salida en el medio de la secuencia es

strm->state->pending + deflateBound(strm, strm->avail_in) 

la primera parte representa los datos todavía en el tubo de llamadas anteriores para desinflar(), la segunda parte representa los datos aún no procesados ​​de longitud avail_in.

+0

Has acertado en el comentario de mi respuesta ahora eliminada. Me estaba olvidando del estado interno. Por curiosidad, miré el valor pendiente después de la primera llamada para desinflar en una prueba rápida. El avail_in era cero, avail_out era 2 y pendiente era cero (0). No parece reflejar la cantidad real de datos pendientes. La siguiente llamada para desinflar para descargarlo arrojó ~ 8K a la salida. Entonces esa puede no ser una medida precisa ... en al menos una situación. –

+0

Usted dice que 'strm-> state-> pending' es el tamaño de * los datos que aún están en la tubería *. Si lo entiendo correctamente, este tamaño aumenta con cada llamada a 'desinflar()', hasta que se alcanza un límite superior desconocido. Y este límite superior es precisamente lo que estoy buscando. Entonces, ¿cómo es esto útil? ¿Me estoy perdiendo de algo? – feklee

+0

Quiero decir, que si le das a deflate() un búfer de tamaño strm-> state-> pending + deflateBound (strm, strm-> avail_in) nunca se quedará sin espacio en el búfer. –

Cuestiones relacionadas