I have a large binary buffer in PHP script, finding a specific position. I need to convert 4-byte value to 32bit integer.
$seg = hex2bin("AABBCC00010014AABBCC");
$findStart=3;
echo bin2hex($seg[$findStart+0]);
echo bin2hex($seg[$findStart+1]);
echo bin2hex($seg[$findStart+2]);
echo bin2hex($seg[$findStart+3]);
Prints:
00010014
I need to convert seg[findStart+0 .. findStart+3] to 32bit integer. How to do it in PHP script? This example is a decimal number 65556.
This is exactly the purpose of the unpack function, which takes a format string and extracts data from a binary string.
Looking at the list of format codes, and your example, I believe you want
N: unsigned long (always 32 bit, big endian byte order)
So it would look something like this:
$int = unpack('Nvalue', $seg, $findStart)['value'];
For compatibility with older versions of PHP (<7.1), you can emulate the offset argument by consuming a fixed number of bytes into an ignored variable:
$int = unpack("c{$findStart}ignore/Nvalue", $seg)['value'];
You can use ord() to convert a single byte into an integer, then use the left shift and the bitwise-or operator. The first byte you will shift 24 bits to the left, the second byte you will shift 16 bytes to the left, the third 8 bytes.
$a = ord($seg[$findStart+0]);
$b = ord($seg[$findStart+1]);
$c = ord($seg[$findStart+2]);
$d = ord($seg[$findStart+3]);
$newInt = ($a << 24) | ($b << 16) | ($c << 8) | $d;
Related
Operations on bits.
How to take 2 bits from byte like this:
take first 2 from 12345678 = 12;
Make new byte = 00000012
For example as asked in discussion by jspit :
$char = 'z'; //is 122, 0111 1010
$b = $char & '?'; // ? is 63, 0011 1111
echo $b; //$b becomes 58 and shows ':'
//if integer used you get:
$b = $char & 63;// 63 is 0011 1111 as '?' but $char is string and you get 0 result:
echo $b; //$b becomes 0 because conversion to integer is used from string and $char becomes 0 and get 0 & 63 = 0, and here is error.
For clearance operation is on bits not on bytes, but bits from bytes.
'string' >> 1 not work, but this is second problem.
Codes of char You can check on my site generating safe readable tokens, with byte template option on. Site is in all available languages.
I think I found good answer here:
how to bitwise shift a string in php?
PS. Sorry I cant vote yours fine answers but I have no points reputation here to do this ;)...
I hope you understand bits can only be 0 or 1, I'm assuming when you say "12345678" you're just using those decimal symbols to represent the positions of each bit. If that is the case, then you're looking for bitwise operators.
More specifically:
$new = $old >> 6;
This bitwise shift operation will shift all bits 6 positions to the right, discarding the 6 bits that were there before.
You can also use an or operation with a bitmask to ensure only 2 bits remain, in case the variable had more than 8 bits set:
$new = ($old >> 6) | 0b00000011;
function highestBitsOfByte(int $byte, int $count = 2):int {
if($count < 0 OR $count > 8) return false; //Error
return ($byte & 0xFF) >> (8-$count);
}
$input = 0b10011110;
$r = highestBitsOfByte($input,2);
echo sprintf('%08b',$r);
The integer number is limited to the lowest 8 bits with & 0xFF. Then the bits are shifted to the right according to the desired length.
example to try: https://3v4l.org/1lAvO
If there is a character as input and the fixed number of 2 bits is required, then this can be used:
$chr = 'z'; //0111 1010
$hBits = ord($chr) >> 6;
echo sprintf('%08b',$hBits); //00000001
I have this code in Qt c++
const unsigned char *packed = reinterpret_cast<const unsigned char*>(data.constData());
res.type = static_cast<int>(packed[0]);
res.period = static_cast<int>(packed[1]);
res.rate = static_cast<qint16>(packed[2] | (packed[3] << 8)) / 100.;
res.edge = static_cast<qint16>(packed[4] | (packed[5] << 8)) / 100.;
return res;
How to convert it from c++ to php
I try this:
$a = unpack ("C*", $data);
$eventList = [];
for ($i=0; $i < $a[1]; $i++)
{
$event = array ();
$index = $i * 6 + 2;
$event["type"] = $a[$index];
$event["period"] = $a[$index+1];
$event["rate"] = ($a[$index+2] | ($a[$index+3] << 8)) / 100;
$event["edge"] = ($a[$index+4] | ($a[$index+5] << 8)) / 100;
}
Edge conver wrong
Very big value.
[edge] => 650.86
must be -4.5
Type, period and rate is good;
Help me please
Don't know the exact answer but some of possible ways to solve the problem:
Check $a[$index+4] and $a[$index+5] value by using var_dump to get its value and type:
var_dump($a[$index+4]);
var_dump($a[$index+5]);
is the data type and its value as expected? Probably good idea is to check as above all data before/after calculation to exactly know what you are dealing with.
Double check your conversion type, perhaps you should't use C* but other, perhaps S or s?
conversion types
If you need type conversion in php you can check how it is done here: Type Juggling and Casting
Note that in PHP you can use a string with ASCII digit that can be treated as digit for calculations:
$foo = 5 * "10 Little Piggies"; // $foo is integer (50)
Which is something you probably don't want.
If you expect negative value but you get positive you have problem because your'e not setting MSB by shifting bits:
The MSB can also correspond to the sign bit of a signed binary number
read-wiki
in case packed[5] should be negative but it isn't
If this not helps then provide data sample and expected values for Edge, Type, period and rate.
I am trying to xor two values which are like below:
Variable 1 : 6463334891
Variable 2 : 1000212390
When i did xor with these values in php it gives me wrong answer.
It should give me "7426059853"
This is my code
$numericValue = (int)$numericValue;
$privateKey = (int)$privateKey;
echo "Type of variable 1 ".gettype($numericValue)."<br />";
echo "Type of variable 2 ".gettype($privateKey)."<br />";
$xor_val = (int)$numericValue ^ (int)$privateKey;
echo "XOR Value :".$xor_val."<br />";
Just a total stab into the dark...
You're doing this:
echo "6463334891" ^ "1000212390";
When you want to be doing this:
echo 6463334891 ^ 1000212390;
XOR is an operation on bytes. The byte representation of the integer 6463334891 and the string "6463334891" are very different. Hence this operation will result in very different values depending on whether the operands are strings or integers. If you get your numbers in string form, cast them to an int first:
echo (int)$var1 ^ (int)$var2;
That is because you re hitting the MAXIMUM INTEGER LIMIT which is 2147483647
From the PHP Docs...
The maximum value depends on the system. 32 bit systems have a maximum
signed integer range of -2147483648 to 2147483647. So for example on
such a system, intval('1000000000000') will return 2147483647. The
maximum signed integer value for 64 bit systems is
9223372036854775807.
Thus to handle such big integers you need to make use of an extension like (GMP) GNU Multiple Precision
<?php
$v1="6463334891";
$v2="1000212390";
$a = gmp_init($v1);
$b = gmp_init($v2);
echo gmp_intval($a) ^ gmp_intval($b); //"prints" 7426059853
Else , Switch to a 64-bit system.
my solution to maintain the value of big integers is to convert them to binary (with base_convert cause decbin doesnt work) and then make the xor for every bit, to finally convert the string to decimal.
function binxor($w1,$w2)
{
$x=base_convert($w1, 10, 2);
$y=base_convert($w2, 10, 2);
// adjust so both have same lenght
if (strlen($y)<strlen($x)) $y=str_repeat(0,strlen($x)-strlen($y)).$y;
if (strlen($x)<strlen($y)) $x=str_repeat(0,strlen($y)-strlen($x)).$x;
$x=str_split($x);$y=str_split($y);
$z="";
for ($k=0;$k<sizeof($x);$k++)
{
// xor bit a bit
$z.=(int)($x[$k])^(int)($y[$k]);
}
return base_convert($z,2,10);
}
Also, to adjust large numbers to 32 bits
bindec(decbin($number))
because decbin cuts the number to 32 automatically.
I have a 2 byte long hex string and need to bitwise shift the 1st one a byte to the left and then add the 2nd byte to it. Both strings come from a 32 character long string. The strings come from a data file I am parsing.
$hex="05E000752F0100D0A500503891FB199A"; //example line of data from file
$vcanvbatt=(base_convert(((base_convert(substr($hex,12,2),16,2)<<8)+base_convert(substr($hex,14,2),16,2)),2,10))/100;
You don't need to convert to binary at all, simply shifting decimal numbers will do. If I understand the math you need correctly, this should work:
$byte1 = hexdec(substr($hex, 12, 2));
$byte2 = hexdec(substr($hex, 14, 2));
$result = ($byte1 << 8) + $byte2;
I'm having a problem with converting binary strings to signed integers
If you call decbin('-40'), php will output 1111111111111111111111111111111111111111111111111111111111011000
But if you call bindec(decbin('-40')), it will output 1.84467440737E+19 (or something similar, which is obviously not -40) because it "sees the most significant bit as another order of magnitude rather than as the sign bit" - php manual
Is there a way to convert a binary 64 bit binary string (much like the one output by decbin) string into a signed integer?
From the documentation, you cannot use bindec
bindec() interprets all binary_string values as unsigned integers. This is because bindec() sees the most significant bit as another order of magnitude rather than as the sign bit.
base_convert appears to ignore signing altogether.
If you know that your incoming string will always be 64 bit binary and you are not on a 32 bit system, it's quite easy to write a custom function.
Check if the string is 64 characters long.
Check if the most significant bit is a 1.
Flip all bits
Add 1
Negate
Here's a quick one I knocked together.
function bindec2($bin)
{
if (strlen($bin) == 64 && $bin[0] == '1') {
for ($i = 0; $i < 64; $i++) {
$bin[$i] = $bin[$i] == '1' ? '0' : '1';
}
return (bindec($bin) + 1) * -1;
}
return bindec($bin);
}
Use pack and unpack to convert between int and binary string.
// 'i' means signed integer
var_dump(unpack('i', pack('i', '-40'))); // gives you -40