/************************************************************************** * * huffman.c -- Huffman coding functions * Copyright (C) 1994 Neil Sharman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * **************************************************************************/ #include "sysfuncs.h" #include "memlib.h" #include "huffman.h" #include "messages.h" #include "netorder.h" /* [RPAP - Jan 97: Endian Ordering] */ /* ->data on success, NULL on error */ huff_data * Generate_Huffman_Data (int num, long *freqs, huff_data * data, u_long * mem) { int HNum, i, count; unsigned long *heap; huff_data *hd = data; if (!hd) { if (!(hd = new huff_data)) goto error0; } if (!(heap = new unsigned long[num * 2])) goto error1; if (!(hd->clens = new char[num])) goto error2; hd->num_codes = num; if (mem) { if (hd != data) *mem += sizeof (huff_data); *mem += num * sizeof (char); } memcpy (&heap[num], freqs, num * sizeof (*heap)); memset (heap, '\0', num * sizeof (*heap)); /* Initialise the pointers to the leaves */ for (count = i = 0; i < num; ++i) if (heap[num + i]) heap[count++] = num + i; /* Reorganise the pointers so that it is a heap */ HNum = count; for (i = HNum / 2; i > 0; --i) { register int curr, child; curr = i; child = curr * 2; while (child <= HNum) { if (child < HNum && heap[heap[child]] < heap[heap[child - 1]]) ++child; if (heap[heap[curr - 1]] > heap[heap[child - 1]]) { register u_long temp; temp = heap[child - 1]; heap[child - 1] = heap[curr - 1]; heap[curr - 1] = temp; curr = child; child = 2 * curr; } else break; } } /* Form the huffman tree */ while (HNum > 1) { int pos[2]; for (i = 0; i < 2; ++i) { register int curr, child; pos[i] = heap[0]; heap[0] = heap[--HNum]; curr = 1; child = 2; while (child <= HNum) { if (child < HNum && heap[heap[child]] < heap[heap[child - 1]]) ++child; if (heap[heap[curr - 1]] > heap[heap[child - 1]]) { register int temp; temp = heap[child - 1]; heap[child - 1] = heap[curr - 1]; heap[curr - 1] = temp; curr = child; child = 2 * curr; } else break; } } heap[HNum + 1] = heap[pos[0]] + heap[pos[1]]; heap[pos[0]] = heap[pos[1]] = HNum + 1; heap[HNum] = HNum + 1; { register int parent, curr; ++HNum; curr = HNum; parent = curr >> 1; while (parent && heap[heap[parent - 1]] > heap[heap[curr - 1]]) { register int temp; temp = heap[parent - 1]; heap[parent - 1] = heap[curr - 1]; heap[curr - 1] = temp; curr = parent; parent = curr >> 1; } } } /* Calculate the code lens */ heap[0] = -1UL; heap[1] = 0; for (i = 2; i < num * 2; ++i) heap[i] = heap[heap[i]] + 1; /* zero the lencount's array */ memset (hd->lencount, '\0', sizeof (hd->lencount)); hd->maxcodelen = 0; hd->mincodelen = 100; /* Set the code length of each leaf in the huffman tree */ for (i = 0; i < num; ++i) { register u_long codelen = heap[i + num]; hd->clens[i] = (char) codelen; if (!codelen) continue; if (codelen > hd->maxcodelen) hd->maxcodelen = codelen; if (codelen < hd->mincodelen) hd->mincodelen = codelen; ++hd->lencount[codelen]; } if (hd->maxcodelen == 0 && hd->mincodelen == 100) { hd->num_codes = 0; } else { /* Calculate the current codes for each different code length */ hd->min_code[hd->maxcodelen] = 0; for (i = hd->maxcodelen - 1; i>=0; --i) hd->min_code[i] = (hd->min_code[i + 1] + hd->lencount[i + 1]) >> 1; } delete []heap; return (hd); error2: delete []heap; error1: if (!data) delete hd; error0: return (NULL); } unsigned long * Generate_Huffman_Codes (huff_data * data, u_long * mem) { int i; unsigned long *codes; unsigned long mc[MAX_HUFFCODE_LEN + 1]; if (!data) return (NULL); if (!(codes = new unsigned long[data->num_codes])) return (NULL); if (mem) *mem += data->num_codes * sizeof (*codes); memcpy (mc, data->min_code, sizeof (mc)); for (i = 0; i < data->num_codes; ++i) if (data->clens[i]) codes[i] = mc[(int) (data->clens[i])]++; return (codes); } unsigned long ** Generate_Huffman_Vals (huff_data * data, u_long * mem) { int i; unsigned long *fcode[MAX_HUFFCODE_LEN + 1]; unsigned long **values; unsigned long *vals; if (!data) return (NULL); if (!(vals = new unsigned long[data->num_codes])) return (NULL); if (!(values = new unsigned long *[MAX_HUFFCODE_LEN + 1])) { delete []vals; return (NULL); } memset (values, '\0', (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *)); if (mem) *mem += data->num_codes * sizeof (*vals) + (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *); fcode[0] = values[0] = &vals[0]; for (i = 1; i <= data->maxcodelen; ++i) fcode[i] = values[i] = &vals[(values[i - 1] - vals) + data->lencount[i - 1]]; for (i = 0; i < data->num_codes; ++i) if (data->clens[i]) *fcode[(int) (data->clens[i])]++ = i; return (values); } double Calculate_Huffman_Size (int num, long *freqs, long *counts) { double size = 0; huff_data hd; int i; if (!Generate_Huffman_Data (num, freqs, &hd, NULL)) return -1; for (i = 0; i < num; ++i) size += counts[i] * hd.clens[i]; delete []hd.clens; return size; } int Write_Huffman_Data (FILE * f, huff_data * hd) { /* [RPAP - Jan 97: Endian Ordering] */ HTONSI(hd->num_codes); HTONSI(hd->mincodelen); HTONSI(hd->maxcodelen); if (fwrite (&hd->num_codes, sizeof (hd->num_codes), 1, f) != 1) return -1; if (fwrite (&hd->mincodelen, sizeof (hd->mincodelen), 1, f) != 1) return -1; if (fwrite (&hd->maxcodelen, sizeof (hd->maxcodelen), 1, f) != 1) return -1; /* [RPAP - Jan 97: Endian Ordering] */ NTOHSI(hd->num_codes); NTOHSI(hd->mincodelen); NTOHSI(hd->maxcodelen); if (hd->num_codes) { /* [RPAP - Jan 97: Endian Ordering] */ int i; for (i = hd->mincodelen; i < hd->maxcodelen + 1; ++i) HTONSI(hd->lencount[i]); for (i = 0; i < hd->maxcodelen + 1; ++i) HTONUL(hd->min_code[i]); if (fwrite (hd->lencount + hd->mincodelen, sizeof (*hd->lencount), hd->maxcodelen - hd->mincodelen + 1, f) != hd->maxcodelen - hd->mincodelen + 1) return -1; if (fwrite (hd->min_code, sizeof (*hd->min_code), hd->maxcodelen + 1, f) != hd->maxcodelen + 1) return -1; if (fwrite (hd->clens, sizeof (*hd->clens), hd->num_codes, f) != hd->num_codes) return -1; /* [RPAP - Jan 97: Endian Ordering] */ for (i = hd->mincodelen; i < hd->maxcodelen + 1; ++i) NTOHSI(hd->lencount[i]); for (i = 0; i < hd->maxcodelen + 1; ++i) NTOHUL(hd->min_code[i]); } return 0; } int Read_Huffman_Data (FILE * f, huff_data * hd, u_long * mem, u_long * disk) { if (fread (&hd->num_codes, sizeof (hd->num_codes), 1, f) != 1) return -1; if (fread (&hd->mincodelen, sizeof (hd->mincodelen), 1, f) != 1) return -1; if (fread (&hd->maxcodelen, sizeof (hd->maxcodelen), 1, f) != 1) return -1; /* [RPAP - Jan 97: Endian Ordering] */ NTOHSI(hd->num_codes); NTOHSI(hd->mincodelen); NTOHSI(hd->maxcodelen); if (disk) (*disk) += sizeof (hd->num_codes) + sizeof (hd->mincodelen) + sizeof (hd->maxcodelen); hd->clens = NULL; if (hd->num_codes) { int i; /* [RPAP - Jan 97: Endian Ordering] */ memset (hd->lencount, '\0', sizeof (hd->lencount)); if (fread (hd->lencount + hd->mincodelen, sizeof (*hd->lencount), hd->maxcodelen - hd->mincodelen + 1, f) != hd->maxcodelen - hd->mincodelen + 1) return -1; /* [RPAP - Jan 97: Endian Ordering] */ for (i = hd->mincodelen; i < hd->maxcodelen + 1; ++i) NTOHSI(hd->lencount[i]); if (disk) (*disk) += sizeof (*hd->lencount) * (hd->maxcodelen - hd->mincodelen + 1); memset (hd->min_code, '\0', sizeof (hd->min_code)); if (fread (hd->min_code, sizeof (*hd->min_code), hd->maxcodelen + 1, f) != hd->maxcodelen + 1) return -1; /* [RPAP - Jan 97: Endian Ordering] */ for (i = 0; i < hd->maxcodelen + 1; ++i) NTOHUL(hd->min_code[i]); if (disk) (*disk) += sizeof (*hd->min_code) * (hd->maxcodelen + 1); if (!(hd->clens = new char[hd->num_codes])) return -1; if (fread (hd->clens, sizeof (*hd->clens), hd->num_codes, f) != hd->num_codes) return -1; if (disk) (*disk) += sizeof (*hd->clens) * hd->num_codes; } if (mem) *mem += sizeof (*hd->clens) * hd->num_codes; return 0; }