Está en la página 1de 6

Trabajo Práctico 1

Compresión de Archivos
Organización del Computador 2

2do. Cuatrimestre 2010

1. Introdución teórica
En Ciencias de la Computación y Teorı́a de la Información, la codificación de Huffman es
una técnica utilizada para compresión de datos. La idea de esta técnica es generar una tabla
de códigos de longitud variable para codificar cada sı́mbolo (como puede ser un carácter o
byte en un archivo). La tabla se rellena basándose en la probabilidad estimada de aparición
de cada posible valor de dicho sı́mbolo. Esta técnica fue desarrollada por David A. Huffman
mientras era estudiante de doctorado en el MIT, y publicada en el artı́culo “A Method for the
Construction of Minimum-Redundancy Codes”.

La codificación Huffman usa un método especı́fico para elegir la representación de cada


sı́mbolo, que da lugar a un código libre de prefijo1 que representa los sı́mbolos más comunes
usando las cadenas de bits más cortas, y viceversa.

La compresión se realiza al reemplazar cada sı́mbolo por su respectiva codificación, siguien-


do la tabla de códigos. Como los sı́mbolos que se presentan con mayor frecuencia se reemplazan
por cadenas de bits más cortas, es de esperar que el resultado sea una cadena de bits más
corta que la original.

Además, como el código que se obtiene es libre de prefijos, dado una cadena de bits que
representa una compresión de datos y su respectiva tabla de códigos, es posible obtener la
cadena de sı́mbolos originales. Por esto, decimos que esta técnica de compresión de datos es
sin perdida de información.

Para la obtención de la codificación de Huffman se utiliza el siguiente algoritmo que


consiste en la creación de un árbol binario que tiene por hoja cada uno de los sı́mbolos:

1. Se crea una lista de árboles, uno por cada uno de los sı́mbolos. Cada árbol consiste en
un nodo sin hijos, y etiquetado cada uno con su sı́mbolo asociado y la frecuencia de
aparición del mismo.
1
Un código libre de prefijo es un código, tı́picamente de longitud variable, donde ninguna palabra del código
es prefijo de cualquier otra palabra. Por ejemplo, un código con las palabras {0, 10, 11} es libre de prefijo;
mientras que un código con las palabras {0, 1, 10, 11} no lo es, porque 1 es prefijo de tanto 10 como 11.

1
2. Se toman los dos árboles de menor frecuencia, y se unen creando un nuevo árbol. La
etiqueta de la raı́z será la suma de las frecuencias de las raı́ces de los dos árboles que se
unen, y cada uno de estos árboles será un hijo del nuevo árbol. También se etiquetan
las dos ramas del nuevo árbol: con un 1 la de la izquierda, y con un 0 la de la derecha.

3. Se repite el paso 2 hasta que sólo quede un árbol.

Con este árbol se puede obtener el código asociado a un sı́mbolo, ası́ como el sı́mbolo
asociado a un determinado código. Para eso se debe proceder del siguiente modo:

1. Se comenza con un código vacı́o.

2. Se inicia el recorrido del árbol en la hoja asociada al sı́mbolo.

3. Se recorre el árbol hacia arriba.

4. Cada vez que se suba un nivel, se añade al código la etiqueta de la rama que se ha
recorrido.

5. Tras llegar a la raı́z, se invierte el código.

6. El resultado es el código Huffman para el sı́mbolo.

2. Enunciado
El objetivo de este trabajo práctico es realizar un programa que dado un archivo lo com-
prima utilizando la codificación de Huffman. El resultado va ser un archivo OC2 que va a tener
un encabezado (header) que incluya datos de la compresión, la tabla de códigos y los datos
comprimidos. También se pide realizar un programa que dado un archivo OC2 lo descomprima.

Las funciones que deben implementar para llevar a cabo esta tarea son las siguientes:

int comprimir archivo ( char *archivo entrada, char *archivo salida )

• Descripción: Dado un archivo de entrada, lo carga en un buffer, lo comprime y


lo guarda en el archivo de salida (el archivo de salida consta de: a) un header, b)
los códigos utilizados para la codificación y c) los datos comprimidos). En caso de
no poder realizar la compresión debe retornar un código de error.
• Funciones auxiliares que utiliza: compimir buffer.

int comprimir buffer ( unsigned char *scr buf, unsigned int scr size,
unsigned char **dst buf, unsigned char **dst size,
codigo t **tabla codigos, unsigned int *long codificacion )

• Descripción: Dado un buffer de entrada, comprime los datos contenidos en él.


Devuelve el puntero al buffer con los datos comprimidos, la longitud de este buffer
en bytes, un puntero a la tabla de códigos utilizada para realizar la codificación y
la longitud en bits de la codificación. En caso de no poder realizar la compresión
debe retornar un código de error.

2
• Funciones auxiliares que utiliza: generar tabla apariciones,
generar tabla codigos, calcular longitud codificacion,
generar codificacion.

unsigned int * generar tabla apariciones ( unsigned char *buf,


unsigned int size )

• Descripción: A partir de un buffer de entrada retorna una tabla de 256 posiciones


donde la posición i contiene la cantidad de apariciones del sı́mbolo i en el buffer.

codigo t * generar tabla codigos ( unsigned int *tabla apariciones )

• Descripción: A partir de la tabla de apariciones, genera la tabla de códigos.


• Funciones auxiliares que utiliza: crear lista huffman,
crear arbol huffman, generar codigo.

void crear lista huffman ( unsigned int *tabla apariciones,


nodo lista t **l, nodo arbol t **indices nodos simbolos )

• Descripción: Dada una tabla de apariciones, retorna una lista de árboles. Cada
árbol de la lista se corresponde con uno de los sı́mbolos de la tabla cuya canti-
dad de apariciones es mayor que cero. Los árboles consisten de un sólo nodo que
está “etiquetado” con el sı́mbolo y la cantidad de apariciones del mismo. La lista
está ordenada por la cantidad de apariciones de los sı́mbolos. La función también
retorna una tabla de 256 posiciones donde la posición i tiene un puntero a la hoja
del árbol para el simbolo i y NULL para aquellos sı́mbolos que no tienen hojas
asociadas (es decir, aquellos sı́mbolos cuya cantidad de apariciones es 0). Está tabla
será utilizada luego para generar los códigos de los sı́mbolos.
• Funciones auxiliares que utiliza: lista insertar ordenado.

void lista insertar ordenado ( nodo lista t **l, nodo lista t *n )

• Descripción: Dada una lista y un nodo, inserta el nodo de manera ordenada, de


acuerdo a la cantidad de apariciones que indica la raı́z del árbol apuntado por él.

void crear arbol huffman ( nodo lista t **l, nodo arbol t **a )

• Descripción: Dada una la lista de árboles, donde cada árbol consta de un nodo
“etiquetado” con un sı́mbolo y la cantidad de apariciones de ese sı́mbolo, genera
el árbol de Huffman. El proceso consta de generar un nuevo árbol a partir de los
dos primeros árboles de la lista. El primero de estos pasa a ser el hijo izquierdo y
el segundo el hijo derecho del nuevo árbol creado. También se debe establecer la
cantidad de aparaciones del árbol recién creado como la suma de las apariciones de
los hijos. Finalmente, este nuevo árbol se inserta de manera ordenada en la lista.
El proceso se repite hasta que quede un solo árbol.
• Funciones auxiliares que utiliza: lista obtener primero,
lista insertar ordenado.

void lista obtener primero ( nodo lista t **l, nodo lista t **n )

3
• Descripción: Retorna el primer elemento de la lista (el nodo se quita de la lista).

int generar codigo ( nodo arbol t *a, nodo arbol t *h, codigo t *c )

• Descripción: Dado un árbol de Huffman y un puntero a la hoja asociada al


sı́mbolo para el cuál se quiere obtener el código, recorre el árbol y retorna el código.
Si la longitud del código generado es mayor a 32 bits, la función debe terminar y
retornar un código de error.

int calcular longitud codificacion ( unsigned int *tabla apariciones,


codigo t *tabla codigo )

• Descripción: Retorna la longitud de la códificación.

int generar codificacion ( unsigned char *src buf, unsigned int src size,
unsigned char *dst buf, unsigned int dst size, codigo t *tabla codigos )

• Descripción: Dado un buffer de entrada y una tabla de códigos, realiza la codifi-


cación y la almacena en el buffer de salida.

int descomprimir archivo ( char *nomb arch entrada,


char *nomb arch salida )

• Descripción: Dado un archivo de entrada, lo carga en un buffer, lo descomprime


y lo guarda en el archivo de salida. En caso de no poder realizar la compresión
debe retornar un código de error.
• Funciones auxiliares que utiliza: cargar tabla codigo desde buffer,
descomprimir buffer.

int cargar tabla codigo desde buffer ( unsigned char * buf,


unsigned int size, codigo t **tabla codigo )

• Descripción: Cargar la tabla de códigos a partir del buffer de entrada.

int descomprimir buffer ( unsigned char *src buf,


unsigned int long codificacion, unsigned char *dst buf,
unsigned int dst size, codigo t *tabla codigos )

• Descripción: Dado un buffer y una tabla de códigos realiza la decodificación y


pone el resultado en el buffer de salida.
• Funciones auxiliares que utiliza: cargar arbol huffman.

int cargar arbol huffman ( codigo t *tabla codigos, nodo arbol t **a )

• Descripción: Cargar el árbol de Huffman a partir del buffer de una tabla de


códigos. El árbol de Huffman se utilizará para facilitar el proceso de descompresión.

Las estructuras de datos utilizadas son:

4
typedef struct {
unsigned char s i m b o l o ;
unsigned i nt a p a r i c i o n e s ;
unsigned i nt v a l i d o ; // i n d i c a s i e l v a l o r almacenado en s i m b o l o e s v a l i d o
void ∗ padre ;
void ∗ i z q ;
void ∗ d e r ;
} a t t r i b u t e (( packed )) nodo arbol t ;

typedef struct {
nodo arbol t ∗ nodo arbol ;
void ∗ s i g ;
} attribute (( packed )) nodo lista t ;

typedef struct {
unsigned i nt c o d i g o ;
unsigned i nt c o d i g o r e v ;
unsigned i nt l o n g c o d i g o ;
unsigned i nt v a l i d o ; // i n d i c a s i l a i n f o r m a c i ó n almacenada e s v a l i d a
} attribute (( packed )) codigo t ;

typedef struct {
unsigned i nt t a m o r i g i n a l ;
unsigned i nt l o n g c o d i f i c a c i o n ;
unsigned i nt c a n t c o d i g o s ;
unsigned i nt o f f s e t c o d i f i c a c i o n ;
} attribute (( packed )) oc2 header t ;

typedef struct {
unsigned char s i m b o l o ;
unsigned i nt c o d i g o ;
unsigned i nt l o n g c o d i g o ;
} attribute (( packed )) codificacion t ;

Notas:

a) Todas las funciones deben estar en lenguaje ensamblador. Cualquier función extra que
necesiten hacer también debe estar hecha en lenguaje ensamblador.

b) Toda la memoria dinámica reservada por la función malloc debe ser correctamente
liberada, utilizando la función free.

c) Para el manejo de archivos se recomienda usar las funciones de C: fopen, fread, fwrite,
fclose, fseek, ftell, ect.

d) Los códigos se almacenan en el archivo comprimido utilizando la estructura


codificacion t. Sólo deben almacenarse los códigos que fueron utilizados para realizar
la codificación.

e) Se recomienda fuertemente hacer testing de cada función a medida que las van haciendo.
Pueden implementar funciones (en C) que impriman por pantalla las tablas de apari-
ciones, tablas de código, lista de árboles, árboles, etc. de modo de ir viendo en cada
momento que están haciendo.

5
3. Informe y forma de entrega
El informe debe reflejar el trabajo hecho para obtener el resultado, las decisiones tomadas
(con el estudio de sus alternativas), las estructuras de datos usadas (con gráficos y/o diagramas
si ayudan a clarificar), las pruebas que hayan hecho para tomar decisiones o para detectar
errores, etc. Debe contar como mı́nimo con los siguientes capı́tulos: Introducción, Desarrollo,
Resultados y Conclusiones. Debe estar estructurado top-down o sea leyendo la introducción
se debe saber qué se hizo y cuáles son las partes más importantes. En el capı́tulo de desarrollo
se debe detallar las decisiones que se tomaron y detallar la implementación de cada una de
las funciones (después de leer los primeros capı́tulos se debe saber cada cosa que se hizo y
como se hicieron las más importantes. En el capı́tulo Desarrollo se debe detallar cada función
realizada en el TP.
Además, el informe debe incluir:

Carátula con número del grupo y los nombres de los integrantes con número de libreta
y email

Manual del usuario

Instrucciones para el corrector, por ejemplo como ensamblar los archivos fuente para
obtener el ejecutable.

Lista de todos los archivos entregados.

La fecha de entrega de este trabajo es Martes 21 de Septiembre, en el horario de clase (de


17 a 22 hs). No se aceptarán trabajos pasada esa fecha. La entrega se realizará en un CD que
debe incluir las siguientes carpetas:

src: Código fuente.

bin: Ejecutable.

enunciado: Este enunciado.

informe: El informe en formato pdf.

tests: Algunos de los archivos con los cuales testearon el programa.

El informe se evalúa de manera independiente del código. Puede reprobarse el informe y


en tal caso deberá ser reentregado para aprobar la materia.

También podría gustarte