Text
[ class tree: Text ] [ index: Text ] [ all elements ]

Source for file HuffmanCompress.php

Documentation is available at HuffmanCompress.php

  1. <?php
  2.  
  3. // {{{ license
  4.  
  5. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  6. //
  7. // +----------------------------------------------------------------------+
  8. // | PHP Version 4                                                        |
  9. // +----------------------------------------------------------------------+
  10. // | Copyright (c) 1997-2002 The PHP Group                                |
  11. // +----------------------------------------------------------------------+
  12. // | This source file is subject to version 2.0 of the PHP license,       |
  13. // | that is bundled with this package in the file LICENSE, and is        |
  14. // | available at through the world-wide-web at                           |
  15. // | http://www.php.net/license/2_02.txt.                                 |
  16. // | If you did not receive a copy of the PHP license and are unable to   |
  17. // | obtain it through the world-wide-web, please send a note to          |
  18. // | license@php.net so we can mail you a copy immediately.               |
  19. // +----------------------------------------------------------------------+
  20. // | Authors: Markus Nix <mnix@docuverse.de>                              |
  21. // |          David Holmes <exaton@free.fr> (original version)            |
  22. // +----------------------------------------------------------------------+
  23. //
  24.  
  25. // }}}
  26.  
  27.  
  28. require_once 'Text/Huffman.php';
  29.  
  30.  
  31. /**
  32.  * Huffman Compression Class
  33.  * 
  34.  * @package Text
  35.  */
  36.  
  37. {   
  38.     // {{{ properties
  39.     /**
  40.      * Size of the input file, in bytes
  41.      * @access protected
  42.      */ 
  43.     protected $_ifsize;
  44.     
  45.     /**
  46.      * Array of letter occurrences
  47.      * @access protected
  48.      */
  49.     protected $_occ;
  50.  
  51.     /**
  52.      * Index of the root of the Huffman tree
  53.      * @access protected
  54.      */
  55.     protected $_hroot;
  56.     
  57.     /**
  58.      * Array of character codes
  59.      * @access protected
  60.      */
  61.     protected $_codes;
  62.     
  63.     /**
  64.      * Array of character code lengths
  65.      * @access protected
  66.      */
  67.     protected $_codelens;
  68.     // }}}
  69.     
  70.  
  71.     // {{{ constructor
  72.     /**
  73.      * Constructor
  74.      *
  75.      * @access public
  76.      */
  77.     public function __construct()
  78.     {
  79.         parent::__construct();
  80.     
  81.         // Initializing compression-specific variables
  82.         $this->_ocarrier = '';
  83.         $this->_ocarlen  = 0;
  84.     }
  85.     // }}}
  86.     
  87.  
  88.     /**
  89.      * Perform compression.
  90.      *
  91.      * @access public
  92.      */
  93.     public function compress()
  94.     {
  95.         if (!$this->_havefiles{
  96.             throw new Exception('Files not provided.');
  97.         }
  98.  
  99.         // Counting letter occurrences in input file
  100.         $this->_countOccurrences();
  101.     
  102.         // Converting occurrences into basic nodes
  103.         // The nodes array has been initialized, as it will be filled with dynamic incrementation
  104.         $this->_occurrencesToNodes();
  105.     
  106.         // Construction of the Huffman tree
  107.         $this->_makeHuffmanTree();
  108.  
  109.         // Constructing character codes
  110.         $this->_makeCharCodes();
  111.     
  112.         // !! No need for 8 bits of nb of chars in alphabet ?? still use $this->nbchars ? NO
  113.         // !! No need for 8+5+codelen bits of chars & codes ?? still use $this->_codelens array ? YES
  114.     
  115.         // Header : passing the Huffman tree with an automatically stopping algorithm
  116.         $this->_transmitTree();
  117.  
  118.         // End of header : number of chars actually encoded, over 3 bytes
  119.         $this->_bitWrite($this->_decBinDig($this->_ifsize24)24);
  120.  
  121.         // Contents: compressed data
  122.         rewind($this->_ifhand);
  123.     
  124.         while (($char fgetc($this->_ifhand)) !== false{
  125.             $this->_bitWrite($this->_codes[$char]$this->_codelens[$char]);
  126.         }
  127.     
  128.         // Finalising output, closing file handles
  129.         $this->_bitWriteEnd();
  130.     
  131.         fclose($this->_ofhand);
  132.         fclose($this->_ifhand);
  133.     }
  134.  
  135.     /**
  136.      * setFiles() is called to specify the paths to the input and output files.
  137.      * It calls a parent function for its role, then sets some compression-
  138.      * specific variables concerning files.
  139.      *
  140.      * @access public
  141.      */
  142.     public function setFiles($ifile ''$ofile '')
  143.     {
  144.         // Calling the parent function for this role
  145.         parent::setFiles($ifile$ofile);
  146.     
  147.         // Setting compression-specific variables concerning files
  148.         $this->_ifsize = filesize($this->_ifile);
  149.     }
  150.     
  151.     /**
  152.      * Show info on characters codes created from the Huffman tree.
  153.      *
  154.      * @access public
  155.      */
  156.     public function getSCodes()
  157.     {   
  158.         // Sorting codes
  159.         arsort($this->_occ);
  160.     
  161.         // Preparing informative $scodes array
  162.         foreach ($this->_occ as $char => $nbocc)
  163.         {
  164.             $tmp '';
  165.     
  166.             if (ord($char>= 32{
  167.                 $schar $char;
  168.             else {
  169.                 $schar ;
  170.                 $tmp   ' (ASCII : ' ord($char')';
  171.             }
  172.         
  173.             $nboccprefix '';
  174.             
  175.             for ($i = 0; $i < 6 - strlen($nbocc)$i++{
  176.                 $nboccprefix .= '0';
  177.             }
  178.         
  179.             $occpercent round($nbocc $this->_ifsize * 1002);
  180.             $scodes[$schar'(' $nboccprefix $nbocc ' occurences, or ' $occpercent '%) ' $this->_codes[$char' (code on ' $this->_codelens[$char' bits)' $tmp;
  181.         }
  182.  
  183.         return $scodes;
  184.     }
  185.  
  186.     /**
  187.      * Calculate compression ration.
  188.      *
  189.      * @access public
  190.      */
  191.     public function getCompressionRatio()
  192.     {
  193.         // Simulating output file size
  194.         $csize = 0;
  195.     
  196.         foreach ($this->_occ as $char => $nbocc)
  197.             $csize += $nbocc $this->_codelens[$char];
  198.     
  199.         $nbchars count($this->_occ);
  200.  
  201.         $csize += 16 * ($nbchars - 1)// For Huffman tree in header
  202.         $csize += 24; // For nb. chars to read
  203.     
  204.         $csize  ceil($csize / 8);
  205.         $cratio round($csize $this->_ifsize * 1002);
  206.     
  207.         return $cratio;
  208.     }
  209.     
  210.     
  211.     // private methods
  212.     
  213.     /**
  214.      * Count character occurrences in the file, to identify information
  215.      * quantities and later construct the Huffman tree.
  216.      *
  217.      * @access private
  218.      */
  219.     private function _countOccurrences()
  220.     {
  221.         while (($char fgetc($this->_ifhand)) !== false{   
  222.             if (!isset($this->_occ[$char])) {
  223.                 $this->_occ[$char= 1;
  224.             else {
  225.                 $this->_occ[$char]++;
  226.             }
  227.         }
  228.     }
  229.  
  230.     /**
  231.      * Convert the character occurrences to basic Nodes of according weight.
  232.      *
  233.      * @access private
  234.      */
  235.     private function _occurrencesToNodes()
  236.     {
  237.         foreach ($this->_occ as $char => $nboccs)
  238.         {
  239.             $node = array(
  240.                 '_char'   => $char,
  241.                 '_w'      => $nboccs,
  242.                 '_par'    => -1,
  243.                 '_child0' => -1,
  244.                 '_child1' => -1,
  245.                 '_lndone' => false
  246.             );
  247.             
  248.             $this->_nodes[$node;
  249.             
  250.         }
  251.     }
  252.  
  253.     /**
  254.      * Get the index of the first node of lightest weight in the nodes array.
  255.      *
  256.      * @access private
  257.      */
  258.     private function _findLightestNode()
  259.     {
  260.         $minw_nodenum = -1;
  261.         $minw = -1;
  262.     
  263.         foreach ($this->_nodes as $nodenum => $node)
  264.         {
  265.             if (!$node['_lndone'&& ($minw == -1 || $node['_w'$minw)) {
  266.                 $minw $node['_w'];
  267.                 $minw_nodenum $nodenum;
  268.             }
  269.         }
  270.     
  271.         return $minw_nodenum;
  272.     }
  273.  
  274.     /**
  275.      * Create the Huffman tree, after the following algorithm :
  276.      * - Find the two nodes of least weight (least info value)
  277.      * - Set each one's parent to the index a new node which has a weight equal to the sum of weights of the two
  278.      * - At the same time, specify the new nodes children as being the two lightest nodes
  279.      * - Eliminate the two lightest nodes from further searches for lightest nodes
  280.      *
  281.      * This carries on until there is only one node difference between nodes
  282.      * constructed and nodes done : the root of the tree.
  283.      *
  284.      * By following the tree from root down to leaf, by successive children 0 or
  285.      * 1, we can thereafter establish the code for the character.
  286.      *
  287.      * @access private
  288.      */
  289.     private function _makeHuffmanTree()
  290.     {
  291.         $nbnodes     count($this->_nodes);
  292.         $nbnodesdone = 0;
  293.     
  294.         while ($nbnodesdone $nbnodes - 1{
  295.             // Find two lightest nodes and consider them done
  296.             for ($i = 0; $i < 2; $i++{
  297.                 $ln[$i$this->_findLightestNode();
  298.                 $this->_nodes[$ln[$i]]['_lndone'= true;
  299.             }
  300.         
  301.             $nbnodesdone += 2;
  302.         
  303.             // Link them with a parent node of sum weight
  304.             // (whose parent is as yet unknown ; in the case of root, it will stay with -1)
  305.             $node = array(
  306.                 '_char'   => '',
  307.                 '_w'      => $this->_nodes[$ln[0]]['_w'$this->_nodes[$ln[1]]['_w'],
  308.                 '_par'    => -1,
  309.                 '_child0' => $ln[0],
  310.                 '_child1' => $ln[1],
  311.                 '_lndone' => false
  312.             );
  313.             
  314.             $this->_nodes[$node;
  315.             
  316.             $this->_nodes[$ln[0]]['_par'$nbnodes// The number of nodes before incrementation is the index
  317.             $this->_nodes[$ln[1]]['_par'$nbnodes// of the node which has just been created
  318.         
  319.             $nbnodes++;
  320.         }
  321.     
  322.         // Note that the last node is the root of the tree
  323.         $this->_hroot = $nbnodes - 1;
  324.     }
  325.  
  326.     /**
  327.      * Read the Huffman tree to determine character codes.
  328.      *
  329.      * @access private
  330.      */
  331.     private function _makeCharCodes()
  332.     {
  333.         // Note : original alphabet is the keys of $occ
  334.         $i = 0;
  335.     
  336.         foreach ($this->_occ as $char => $nbocc{
  337.             $code    '';
  338.             $codelen = 0;
  339.         
  340.             // Following tree back up to root
  341.             // (therefore _pre_positionning each new bit in the code)
  342.             // $this->nodes[$i] is the original Node of $char
  343.             $curnode $i;
  344.         
  345.             do {
  346.                 $parnode $this->_nodes[$curnode]['_par'];
  347.                 $code    (($this->_nodes[$parnode]['_child0'== $curnode)'0' '1'$code;
  348.                 $codelen++;
  349.                 $curnode $parnode;
  350.             while ($curnode != $this->_hroot);
  351.         
  352.             $this->_codes[$char]    $code;
  353.             $this->_codelens[$char$codelen;
  354.         
  355.             $i++;
  356.         }
  357.     }
  358.  
  359.     /**
  360.      * Transmit Huffman tree.
  361.      *
  362.      * @access private
  363.      */
  364.     private function _transmitTree()
  365.     {
  366.         // Launching the business, specifying that we are starting at root  
  367.         $this->_transmitTreePart($this->_hroottrue);
  368.     }
  369.     
  370.     /**
  371.      * Transmit the Huffman tree in header.
  372.      *
  373.      * @access private
  374.      */
  375.     private function _transmitTreePart($nodenum$isroot)
  376.     {
  377.         // Transmitting current node representation, if we are not working with root (that's only the first time).
  378.         // Then looking at children if appropriate (gee that sounds bad).
  379.         $curnode $this->_nodes[$nodenum];
  380.         $char    $curnode['_char'];
  381.             
  382.         if ($char === ''{
  383.             // Branch Node
  384.             // Being root can only be in this case
  385.         
  386.             if (!$isroot{
  387.                 $this->_bitWrite($this->_nodeChar8);
  388.             }
  389.         
  390.             // Looking at children
  391.             $this->_transmitTreePart($curnode['_child0']false);
  392.             $this->_transmitTreePart($curnode['_child1']false);
  393.         else {
  394.             // Leaf Node
  395.             // Just transmitting the char
  396.             $this->_bitWrite($this->_decBinDig(ord($char)8)8);
  397.         }
  398.     }
  399. }
  400.  
  401. ?>

Documentation generated on Mon, 11 Mar 2019 13:54:22 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.