use string concatenation instead of pack()
When packing bytes the packed binary data (string) may be produced by simply using chr()
, concatenation .
and a foreach
loop:
$packed = "";
foreach ($a as $byte) {
$packed .= chr($byte);
}
Where $a
is the source array and $packed
is the produced binary data stored in a string variable, as per the original question.
benchmark
Having come, at the time of writing, with 5 different working solutions, it's worth doing a benchmark in case the amount of data to pack is huge.
I've tested the five cases with a 1048576 elements array in order to produce 1 MB of binary data. I measured execution time and consumed memory.
2023 (update)
Testing environment: PHP 8.2.7
- macOS Ventura
- CPU Apple M1 Pro
(only a single core is used of course)
// pack with ... operator: 9 ms
// string concatentation: 10 ms
// call_user_func_array: 12 ms
// multiple pack: 46 ms
// array_reduce: 10918 ms
2018
Testing environment: PHP 5.6.30
- Mac OS X
- 2.2 GHz Intel Core I7
(only a single core is used of course)
// pack with ... operator: 57 ms - 1.3 MB
// string concatentation: 197 ms - 1.3 MB
// call_user_func_array: 249 ms - 1.5 MB
// multiple pack: 298 ms - 1.3 MB
// array_reduce: 39114 ms - 1.3 MB
The ...
operator used directly with the pack
function if by far the fastest solution (accepted answer)
If ...
is not available (PHP version prior to 5.6) the solution proposed by this answer (string concatentation
) is the fastest.
Memory usage is almost the same for every case.
I post the test code if anyone interested.
<?php
// Return elapsed time from epoch time in milliseconds
function milliseconds() {
$mt = explode(' ', microtime());
return ((int)$mt[1]) * 1000 + ((int)round($mt[0] * 1000));
}
// Which test to run [1..5]
$test = $argv[ 1 ];
// Test 1024x1024 sized array
$arr = array();
for( $i = 0; $i < 1024 * 1024; $i++ )
{
$arr[] = rand( 0, 255 );
}
// Initial memory usage and time
$ms0 = milliseconds();
$mem0 = memory_get_usage( true );
// Test 1: string concatentation
if( $test == '1' )
{
$data = "";
foreach ( $arr as $byte ) {
$data .= chr( $byte );
}
$test = "string concatentation";
}
// Test 2: call_user_func_array
if( $test == '2' )
{
$data = call_user_func_array("pack", array_merge(array("c*"), $arr));
$test = "call_user_func_array";
}
// Test 3: pack with ... operator
if( $test == '3' )
{
$data = pack("c*", ...$arr);
$test = "pack with ... operator";
}
// Test 4: array_reduce
if( $test == '4' )
{
$data = array_reduce($arr, function($carry, $item) { return $carry .= pack('c', $item); });
$test = "array_reduce";
}
// Test 5: Multiple pack
if( $test == '5' )
{
$data = "";
foreach ($arr as $item) $data .= pack("c", $item);
$test = "multiple pack";
}
// Output result
$ms = milliseconds() - $ms0;
$mem = round( ( memory_get_usage( true ) - $mem0 ) / ( 1024 * 1024 ), 1 );
echo "$test: $ms ms; $mem MB\n";