;;; 
;;; huffman.lisp
;;; 
;;; Created: 2005-03-12 by Zach Beane <xach@xach.com>
;;; 
;;; Copyright (c) 2005 Zachary Beane, All Rights Reserved
;;;
;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:
;;;
;;;   * Redistributions of source code must retain the above copyright
;;;     notice, this list of conditions and the following disclaimer.
;;;
;;;   * Redistributions in binary form must reproduce the above
;;;     copyright notice, this list of conditions and the following
;;;     disclaimer in the documentation and/or other materials
;;;     provided with the distribution.
;;;
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;;; 
;;; $Id: huffman.lisp,v 1.5 2005/03/20 21:33:49 xach Exp $

(in-package :salza-deflate)
            
;;;
;;; Huffman codes are written out to the stream backwards, so we save
;;; them backwards too.
;;;

(defun reverse-bits (word n)
  (let ((j 0))
    (dotimes (i n j)
      (setf j (logior (ash j 1) (logand #x1 word)))
      (setf word (ash word -1)))))

(defun fixed-huffman-table ()
  "Generate the fixed Huffman code table specified by RFC1951."
  (let ((table (make-array (* 288 2)))
        (i 0))
    (flet ((fill-range (length start end)
             (loop for j from start to end do
                     (setf (aref table i) (reverse-bits j length)
                           (aref table (incf i)) length)
                     (incf i))))
      (fill-range 8 #b00110000  #b10111111)
      (fill-range 9 #b110010000 #b111111111)
      (fill-range 7 #b0000000   #b0010111)
      (fill-range 8 #b11000000  #b11000111)
      table)))


;;; DEFLATE uses special Huffman codes to indicate that there is extra
;;; literal data after the code. The WRITE-LITERAL, WRITE-LENGTH, and
;;; WRITE-DISTANCE functions close over vectors that contain bit
;;; patterns at the even offsets and bit lengths at the odd offsets.
;;;
;;; Since we only deal with encoding with the fixed Huffman table
;;; described in the RFC right now, everything can be precomputed.

(defun save-pair (array i code length)
  "Store CODE and LENGTH in consecutive positions in ARRAY."
  (let ((index (ash i 1)))
    (setf (aref array index) code
          (aref array (1+ index)) length)))

(defun length-table (huffman-table)
  "Compute a table of the (Huffman + extra bits) values for all
possible lengths for the given HUFFMAN-TABLE."
  (let ((table (make-array (* 259 2)))
        (code 257)
        (length 3)
        (extra-bit-counts '(0 0 0 0 0 0 0 0
                            1 1 1 1
                            2 2 2 2
                            3 3 3 3
                            4 4 4 4
                            5 5 5 5
                            0)))
    (flet ((save-value (extra-bit-count extra-value)
             (let ((huffman-value (aref huffman-table (ash code 1)))
                   (huffman-count (aref huffman-table (1+ (ash code 1)))))
               (save-pair table length 
                          (logior huffman-value (ash extra-value huffman-count))
                          (+ huffman-count extra-bit-count)))))
      (dolist (count extra-bit-counts)
        (dotimes (i (expt 2 count))
          (when (< length 258)
            (save-value count i)
            (incf length)))
        (incf code))
      (setf code 285)
      (save-value 0 0))
    table))

(defun distance-table ()
  "Compute a table of the (code + extra bits) values for all possible
distances as specified by RFC1951."
  (let ((table (make-array (* 32769 2)))
        (code 0)
        (distance 1)
        (extra-bit-counts '(0 0 0 0
                            1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
                            10 10 11 11 12 12 13 13)))
    (flet ((save-value (extra-bit-count extra-value)
             (save-pair table distance 
                        (logior (ash extra-value 5) (reverse-bits code 5))
                        (+ 5 extra-bit-count))))
      (dolist (count extra-bit-counts table)
        (dotimes (i (expt 2 count))
          (save-value count i)
          (incf distance))
        (incf code)))))

(let ((lvtable (fixed-huffman-table)))
  (declare (type simple-vector lvtable))
  (defun write-literal (code bitstream)
    "Write the Huffman code for the literal CODE to BITSTREAM."
    (declare (optimize (speed 3) (safety 0))
             (type (integer 0 258) code))
    (write-bits (svref lvtable (ash code 1))
                (svref lvtable (1+ (ash code 1))) 
                bitstream)))

(let ((lvtable (distance-table)))
  (declare (type simple-vector lvtable))
  (defun write-distance (distance bitstream)
    "Write the Huffman code and extra bits for distance DISTANCE to
bitstream."
    (declare (optimize (speed 3) (safety 0))
             (type (integer 0 32768) distance))
    (write-bits (svref lvtable (ash distance 1))
                (svref lvtable (1+ (ash distance 1)))
                bitstream)))

(let ((lvtable (length-table (fixed-huffman-table))))
  (declare (type simple-vector lvtable))
  (defun write-length (length bitstream)
    "Write the 5 bit code and extra bits for the length LENGTH to
BITSTREAM."
    (declare (optimize (speed 3) (safety 0))
             (type (integer 0 258) length))
    (write-bits (svref lvtable (ash length 1))
                (svref lvtable (1+ (ash length 1)))
                bitstream)))



