Question

I'm trying to write a program in PHP which takes a list of 9 bit ints (0-511) and 10 bit ints (0-1023) and writes them as binary to a file then reads it back. for example:

$dataIn = array(array(275, 863), array(7, 93), array(510, 1010));

$binData = writeBin($dataIn);
$dataOut = readBin($binData);

var_dump($dataIn, $dataOut, $binData);


function writeBin($data) {
    $bin = "";
    foreach ($data as $d) {
        //                 9 bit  10 bit
        $bin .= pack('SS', $d[0], $d[1]);
    }

    return $bin;
}

function readBin($bin) {
    $bin = str_split($bin, 4);
    $data = array();
    foreach ($bin as $b) {
        $up = unpack('SS', $b); // im only getting 275, 7 and 510 here
        $data[] = $up;
    }

    return $data;
}

But how can i write these to the binary file/string so each section is 19 bits long, and then split it by the 19 bits when reading it back? So a file with 10 of these records in it should only be 190 bits

Was it helpful?

Solution

I solved this issue by writing my own class to write and read differing bit length numbers to binary:

$listLen = 10;

// get an array of 10 random 9 bit ints
$b9s = range(0, 511);
shuffle($b9s);
$b9s = array_slice($b9s, 0, $listLen); 
// get an array of 10 random 10 bit ints
$b10s = range(0, 1023);
shuffle($b10s);
$b10s = array_slice($b10s, 0, $listLen); 

//build array of input data
$dataIn = array();
for ($i = 0; $i < $listLen; $i++) {
    $dataIn[] = array($b9s[$i], $b10s[$i]);
}

//-------


//initialise
$bitFormat = array(9, 10);
$BC = new BinConverter($bitFormat);

//pack
$BC->addData($dataIn);
$binData = $BC->getBin();

//unpack
$BC->setBin($binData);
$dataOut = $BC->getData();

var_dump($dataOut, $binData);



Class BinConverter {
    private $bitFormat;
    private $maxSectLen;
    private $binary;
    private $binData;
    private $dataOffset;

    // $bitFormat = list of bit lengths e.g. array(9, 10)
    public function __construct($bitFormat) {
        $this->setBitFormat($bitFormat);
        $this->binary = "";
        $this->binData = "";
        $this->dataOffset = 0;
    }

    // $bitFormat = list of bit lengths e.g. array(9, 10)
    public function setBitFormat($bitFormat) {
        $bitFormat = array_merge($bitFormat);
        $this->bitFormat = $bitFormat;
        $this->maxSectLen = 0;
        foreach ($this->bitFormat as $bpos => $bf) {
            $this->bitFormat[$bpos] = (int) $bf;
            $this->maxSectLen += $this->bitFormat[$bpos];
        }
    }

    // $data = list of data sections to convert e.g. array(array(167, 89), array(62, 32), array(325, 975))
    public function addData($data) {
        foreach ($data as $d) {
            $d = array_merge($d);
            if (is_array($d) && count($d) != count($this->bitFormat)) {
                throw new Exception("Invalid data section");
            }

            foreach ($d as $dpos => $dbit) {
                $dbit = (int) $dbit;
                $tbin = decbin($dbit);
                if (strlen($tbin) > $this->bitFormat[$dpos]) {
                    throw new Exception("Data (".$dbit.") too big for ".$this->bitFormat[$dpos]." bit segment");
                }
                $this->binary .= $this->binPad($tbin, $this->bitFormat[$dpos]);
            }
        }
    }

    //returns binary representation of added data
    public function getBin() {
        $bin = "";
        if ($this->binary) {
            $bits = str_split($this->binary, 4);
            foreach ($bits as $bpos => $bit) {
                $bp = bindec($this->binPad($bit, 4));
                $bin .= pack('C', $bp);
            }
        }

        return $bin;
    }

    //$bin = string of binary data
    public function setBin($bin) {
        if (!is_string($bin)) {
            throw new Exception("Invalid binary format");
        }

        $bdata = "";
        foreach (str_split($bin) as $b) {
            $unpacked = unpack('C', $b);
            $binbit = $this->binPad(decbin($unpacked[1]), 4);
            $bdata .= $binbit;
        }
        $this->binData = $bdata;
    }

    //returns unpacked data in the current bit format
    public function getData() {
        $data = array();
        $binlen = strlen($this->binData);

        if ($binlen) {
            $bdata = $this->binData;

            $overflow = $binlen % $this->maxSectLen % 4;
            $lastbit = substr($bdata, -4);
            $overflowBit = substr($lastbit, 0, $overflow);
            $binbit = substr($lastbit, $overflow);
            $bdata = substr($bdata, 0, -4) . $binbit;

            $tdata = str_split($bdata, $this->maxSectLen);
            foreach ($tdata as $d) {
                if (strlen($d) != $this->maxSectLen) {
                    throw new Exception("Invalid binary format");
                }
                $offset = 0;
                $ds = array();
                foreach ($this->bitFormat as $bf) {
                    $ds[] = bindec(substr($d, $offset, $bf));
                    $offset += $bf;
                }
                $data[] = $ds;
            }
        }
        return $data;
    }

    //returns unpacked data in the current bit format for (int) $ni number of sections
    public function getNextSection($ni = null) {
        $data = array();
        $binlen = strlen($this->binData);

        if ($binlen) {
            $n = $ni;
            if ($n === null) {
                $n = 1;
            }
            $glen = $n*$this->maxSectLen;
            $bdata = substr($this->binData, $this->dataOffset, $glen);

            if (strlen($bdata) != $glen) {
                throw new Exception("Invalid data format used");
            }

            $this->dataOffset += $glen;
            if ($this->dataOffset+4 > $binlen) {
                $overflow = $binlen - $this->dataOffset;
                $lastbit = substr($this->binData, -4);
                $overflowBit = substr($lastbit, 0, $overflow);
                $binbit = substr($lastbit, $overflow);
                $bdata = substr($bdata, 0, -(4-$overflow)) . $binbit;
            }

            $tdata = str_split($bdata, $this->maxSectLen);
            foreach ($tdata as $d) {
                if (strlen($d) != $this->maxSectLen) {
                    throw new Exception("Invalid binary format");
                }
                $offset = 0;
                $ds = array();
                foreach ($this->bitFormat as $bf) {
                    $ds[] = bindec(substr($d, $offset, $bf));
                    $offset += $bf;
                }
                $data[] = $ds;
            }

            if ($ni === null) {
                return $data[0];
            }
        }
        return $data;
    }

    private function binPad($var, $a) {
        return str_pad($var, $a, '0', STR_PAD_LEFT);
    }
}

and an example of how to use multiple bit formats in one file:

$listLen1 = rand(1, 20);

// get an array of 10 random 9 bit ints
$b9s = range(0, 511);
shuffle($b9s);
$b9s = array_slice($b9s, 0, $listLen1); 
// get an array of 10 random 10 bit ints
$b10s = range(0, 1023);
shuffle($b10s);
$b10s = array_slice($b10s, 0, $listLen1); 

//build array of input data
$dataIn1 = array();
for ($i = 0; $i < $listLen1; $i++) {
    $dataIn1[] = array($b9s[$i], $b10s[$i]);
}

$listLen2 = rand(1, 20);

// get an array of 10 random 16 bit ints
$b16s = range(0, 65535);
shuffle($b16s);
$b16s = array_slice($b16s, 0, $listLen2); 
// get an array of 10 random 8 bit ints
$b8s = range(0, 255);
shuffle($b8s);
$b8s = array_slice($b8s, 0, $listLen2); 
// get an array of 10 random 14 bit ints
$b14s = range(0, 16383);
shuffle($b14s);
$b14s = array_slice($b14s, 0, $listLen2); 

$dataIn2 = array();
for ($i = 0; $i < $listLen2; $i++) {
    $dataIn2[] = array($b16s[$i], $b8s[$i], $b14s[$i]);
}

//-------

$file = './binfile.bf';


//initialise
$bitFormat1 = array(9, 10);
$bitFormat2 = array(16, 8, 14);

$BC = new BinConvert($bitFormat1);
//pack
$BC->addData($dataIn1);
$BC->setBitFormat($bitFormat2);
$BC->addData($dataIn2);

$binData = $BC->getBin();

file_put_contents($file,$binData);


//unpack
$binData = file_get_contents($file);

$BC->setBin($binData);

$BC->setBitFormat($bitFormat1);
$dataOut1 = $BC->getNextSection($listLen1);
$BC->setBitFormat($bitFormat2);
$dataOut2 = $BC->getNextSection($listLen2);

var_dump($dataOut1, $dataOut2, $binData);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top