<?php

class BitStream {
          private 
$_data;
          private 
$_byte_buff;
          private 
$_byte_offset;
          private 
$_bit_offset;
      function 
__construct($data "") {
              
$this->_data $data;
          
$this->_byte_offset 0;
          
$this->_bit_offset  0;
          
// for write
          
$this->_byte_buff 0;
      }
      
/* read method */
      
function getBits($bit_width) {
        
$value 0;
        for(
$i=0$i$bit_width$i++) {
          
$value <<= 1;
          
$value |= $this->getBit();
        }
        return 
$value;
      }
      function 
getBit() {
              
$shift_width $this->_bit_offset;
          
$bit = (ord($this->_data{$this->_byte_offset}) >> $shift_width) & 0x1;
          
$this->_bit_offset ++;
          if (
$this->_bit_offset == 8) {
            
$this->_byte_offset ++;
            
$this->_bit_offset 0;
          }
          return 
$bit;
      }
      function 
getBytesLE($bytes) {
              
$this->align();
          
$value 0;
          if (
$bytes 4) {
            
fprintf(STDERR"getBytesLE arg must be le 4\n");
            exit(
1);
          }
          for (
$i=$i<$bytes $i++) {
            
$byte $this->getByte();
            if ((
$bytes == 4) && ($i == 3) &&
            ((
$byte 0x80) == 0x80)) {
              
fprintf(STDERR"getByteLE can't handle over 2^31 (byte=$byte)\n");
              exit(
1);
            }
            
$value += $byte << ($i);
          }
          return 
$value;
      }
      function 
getByte() {
              
$this->align();
              
$byte ord($this->_data{$this->_byte_offset});
          
$this->_byte_offset ++;
          return 
$byte;
      }
      function 
getStrings($len null) {
              
$this->align();
          if (
is_null($len)) {
                  
$len strlen($this->_data) - $this->_byte_offset;
          }
          
$str substr($this->_data$this->_byte_offset$len);
          
$this->_byte_offset += $len;
          return 
$str;
      }
      function 
align() {
              if (
$this->_bit_offset 0) {
                  
$this->_byte_offset ++;
              
$this->_bit_offset 0;
          }
      }

      
/* write method */
      
function putBits($bits$bit_width) {
        for (
$i=0$i$bit_width$i++) {
          
$bit = ($bits >> ($bit_width $i 1)) & 0x1;
          
$this->putBit($bit);
        }
        return ;
      }
      function 
putBit($bit) {
        if (
$this->_bit_offset == 7) {
          
$byte $this->_byte_buff $bit;
          
$byte sprintf("%c"$byte);
          
$this->_putChar($byte);
        } else {
          
$this->_byte_buff |= $bit << ($this->_bit_offset);
          
$this->_bit_offset++;
        }
        return ;
    }
      function 
putBytesLE($value$bytes) {
        for(
$i=0$i<$bytes$i++) {
          
$value2 $value 0x100;
          
$this->_putByte($value2);
          
$value  = ($value $value2) / 0x100;
        }
        return ;
      }
      function 
putByte($value) {
        
$this->flush();
        return 
$this->_putByte($value);
      }
      function 
_putByte($value) {
        
$byte pack("C"$value);
        
$this->_putChar($byte);
      }
      function 
_putChar($byte) {
        
$data_len strlen($this->_data);
        if (
$data_len $this->_byte_offset) {
          
printf(STDERR"ERROR: data_len=$data_len < byte_offset=" $this->_byte_offset "\n");
          exit(
0);
        }
        if (
$data_len == $this->_byte_offset) {
          
$this->_data .= $byte;
        } else {
          
$this->_data{$this->_byte_offset} = $byte;
          exit(
1);
        }
        
$this->_byte_buff "\0";
        
$this->_bit_offset 0;
        
$this->_byte_offset++;
      }
      function 
putStrings($str$strlen null) {
        if (
is_null($strlen)) {
          
$strlen strlen($str);
          for (
$i=0$i<$strlen$i++) {
        
$c substr($str$i1);
        
$value ord($c);
        
$this->putByte($value);
          }
        }
      }
      function 
flush() {
        if (
$this->_bit_offset) {
          
$byte sprintf("%c"$this->_byte_buff);
          
$this->_putChar($byte);
          
$this->_bit_offset 0;
          
$this->_byte_buff 0;
          } else {
        
// do nothing;
          
}
        return ;
      }
             function 
getByteOffset() {
              return 
$this->_byte_offset;
      }
      function 
setByteOffset($offset) {
              return 
$this->_byte_offset $offset;
      }
      function 
getData() {
        return 
$this->_data;
      }
}