Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Compresión de Archivos
Organización del Computador 2
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”.
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.
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.
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:
4. Cada vez que se suba un nivel, se añade al código la etiqueta de la rama que se ha
recorrido.
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 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 )
2
• Funciones auxiliares que utiliza: generar tabla apariciones,
generar tabla codigos, calcular longitud codificacion,
generar codificacion.
• 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 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 )
int generar codificacion ( unsigned char *src buf, unsigned int src size,
unsigned char *dst buf, unsigned int dst size, codigo t *tabla codigos )
int cargar arbol huffman ( codigo t *tabla codigos, nodo arbol t **a )
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.
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
Instrucciones para el corrector, por ejemplo como ensamblar los archivos fuente para
obtener el ejecutable.
bin: Ejecutable.