I am working with a project which uses a custom TCP protocol. I have everything down the line, except for one thing: The protocol requires 4 bites before the message, that determine the length of the package. In NodeJS, this is done via:
var b = new Buffer(4);
b.writeUInt32BE(data.length, 0);
But is there such a way in PHP also? I was considering using pack() - but I have no experience with this function. I also need to read a package of the same format, so I also need to be able to obtain the integer from the first 4 bites too.
Using pack is the correct way to go in PHP and here is an example of how to use it to return the information you require:
<?php
$string = 'hello world';
$bytes = pack ( 'N', strlen ( $string ) ); // unsigned 32 bit big endian byte order
?>
To parse the first 4 raw bytes back into a number, this is assuming the a variable named $rawBytes contains the data read from the tcp packet.
<?php
$decoded = unpack ( 'N', substr ( $rawBytes, 0, 4 ); );
$length = end ( $decoded );
?>
One unfortunate gotcha within PHP is that the binary only understands 32bit signed integers on Windows machines and on 32bit Linux/Macintosh/Unix machines.
−2,147,483,648 to 2,147,483,647
64bit PHP binaries running on Linux/Macintosh/Unix machines have the maximum integer value of a signed 64bit integer.
−9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
But using 64bit binary on a Windows machine still has an integer size of a 32bit signed integer.
Anything outside of the integer bounds will be cast to a floating point number.
Related
I currently have a Decimal that is coming through as: 4294960896
This is then converted to Binary with this function:
decbin('4294960896')
Which equals: 11111111111111111110011100000000
I then need to take that binary and convert it to the 32bit signed version which should be '-6400'
I can't seem to find any built in functions that support 32bit signed output.
The hexadecimal representation of the number 4294960896 is FFFFE700h. This should be interpreted as 32 bit signed long.
The functions that support such tasks are pack and unpack.
$i64 = 4294960896;
$i32 = unpack('l',pack('V',$i64))[1];
//int(-6400)
I used
$i64 = unpack('L',pack('l',$decimal))[1];
$t = base_convert($i64,10,2);
$r = str_pad($t,32,'0', STR_PAD_LEFT);
It seemed more logical to use L and l formats for (un)packing, to keep it machine independent. Wrote a lot of tests though, to be sure it works.
I understand Integer size, PHP_INT_MAX is platform dependent.
On 64-bit system I can get a:
$large_number = 9223372036854775807
From what I read from PHP Document Example 3 here, when integer overflows, PHP handles integer as float with a precision of roughly 14 decimals digits, i.e:
Unsigned Int64: 18446744073709551615
PHP will handles as: 1.844674407371E+19
So it seems all precision only kept up to the max of Signed Int64.
Is this really just a limitation of PHP and there is nothing I can do about it?
This is pretty hacky, and I assure you your life will be easier using GMP but it is possible using pack() and a little binary thinking:
<?php
$large_number = 9223372036854775807;
$left = 0xffffffff00000000;
$right = 0x00000000ffffffff;
$l = ($large_number & $left) >>32; //shift 32 bits left
$r = $large_number & $right;
$int64_binary_string = pack('NN', $l, $r);
I haven't tested this, and wouldn't use it in real life, but it's probably close to what you're looking for.
http://tw1.php.net/manual/en/function.pack.php
if you need precision or numbers outside of PHP_INT_MAX / PHP_INT_MIN, learn to use GMP, which will probably keep going until you run out of memory... to give you some idea of how far you can take it,
$large_number = gmp_init("9223372036854775807");
$i=0;
while(true){
echo 'iteration '.$i++.':'.gmp_strval($large_number).'*1337=';
$large_number = gmp_mul($large_number,"1337");
echo gmp_strval($large_number).PHP_EOL;
}
will eventually
iteration 1291:63173727399431265977452467325176816266767500735669001788445953704
99100022309199341754638029233101065727095873054512847649411469159231474984853877
29990385564525384931210393372816147935077346316357322846070505282141866990047417
69795035320095201175780336777052639896516690649784147989039497959127394363606555
18277493016490689369408018504673663458745651065704848764945821685549595325229654
78521759046816461228651950877052534907426557036526546565508337234675673499891633
29265311281809541141691613057522094435618917713573494707844587004419303204290366
80410319411622496279245084808349790649184389864091281089934776245296259423173088
37125998364710920001673776690982404095905353172292803413540568804924330209161221
68502039031454119485450113377479892509776976500478875924900269471621353800015849
16768619446056453580306524878177680668058129358555807454509042635315136209751306
28888008462882394248192470687562440408441365704520299664521180552207604024174563
43137016510143518117853956283018500610190006192251441907450531358798897519852285
89712288688300401723956266383086427686519451058576140512867416675873224992592868
63873656820691379608416040082157797343590944870890093034489537997624441231196880
03716995482080107872348544579795198224539076738023147659657987086535844760748293
51680757422559231216506109885022428024013042708325323490543266063675126192858475
51062320509059668456301511665702512575733290996693385011230330439062943182094596
72982868367513131107811463983324141985257032192551001490775273440704964956322520
07994322409745388858643755238449137799970426027598547825636867768578263720009398
06345603047100479826296034929890108340101488966701293667796312958087571285537092
98292308718879824518810679195685709967212889579496467906343837737351387116485926
96065604380646092415099392666166947273008556519323980631398826013377061548412499
37045775535278706790702274483174254245054418225127782273776637425449169726435461
00864900455001093729251741310689643202116980745998974559375404087598011323913589
37378336888032750432647317316783507777959680594838416103235878202519306235270714
07689264877781232001726371838121263807804664374737613209359850417045192588050394
70657700599839677955302104699239419723871651195923433991338871172082351085772713
85795544605616579911291449220540413076513145849439424174121530477862582225947332
71577283620910275735801384726436002832118799836996365693738642178527175171378406
17487708039971384270639342138853231882316669599224158264716457232654635304792216
85595639961619807370286817974879544351592128186367207420223323272766972084744559
66627509594039497325711708953969586289133165339724320993714241576206582692070672
87281148490183467979899908277488360099016745185705207507885725820344193088663031
74742220273886082811546338253396232117540977001317286430939919216858700761871025
04883648220800173612755757031087544792419044177699287860855715222255510614135023
30706372831160632288499262660942154311052431900104315094339186786507006013357031
08125929293885129152264099174616004246743029176792527824811466605161478045492703
27090040582587712800567514734685257659124144431850835544283278381310315791931357
63459289106249506321560095185194943714045276693796155870508190654607382917421646
15486864936613368395909757833185705018554557948762713677856056206953385065593915
49326215023945424937441813782143778143766220175505533500485707438756395764163323
47714668439588417016028256182365660677880890399143415948539281432076160651247693
72982310275084829961404187585100633245654443972233743905144338234758859827413170
88983027723373315156469586730671906768407758289550985343535503818941377020797426
18604249085624012174154963219214543971133251713032811649089587541472009254502083
21938426925234679535003649815691245256828585977342824147515002509080267191887689
93756807224396178461744274960675671607151017304225081498878127594506757910634207
82366257650930768410626608502254524385838019829644847875689142861722171975269086
27152913039908221672006718321592434013558052896236283594560899204934144043552210
8813052796446077685440711695302241857244755302464656726076289075058391*1337=8446
32735330396026118539488137614033486681484835894553911522401035729672982739951992
59510450846561248771271822738836773072631342658924820547496339499714549977043965
30282959394551897891984120249697406451962655622236761656933974621596222296728397
20183102709193795416428153987614058613458087713533262641419642793700816304805168
68985207407486880443429354748473827987325635935798089498320484478359184559360866
27076583226192391712293067578359927580846468827613754693551137122772118377935650
64416866579070402604224929830477624243882128249086083841362204170859705333927752
53506783887636700979595292482900428172427958399610988487824191523745981361850004
22378394358434742762254571913554781639037404921838294896485533928722618505415775
20468015856906162855718175811402571115916602835577500306211903371964419937747843
68698237621235590531937189523891145666785900034163371124374965082326731487376110
98333333092709828260861059469436406514648183983015665803213913077419107406188372
35707395503957353158240382790401778302613604267141259840425062444532997625763710
49295281541865538168765060653162998657037360956425018150966653699907916926437453
64522455898449750483810932923800543871125123028238779261102286096962295954110422
53300041031861800262087455987369484209627287346984244451204684319717267396169213
64686689162749862681054381010309575068563467271336437198517817577032252061277672
60751210970442593137554100625790557600149517970271550344604758277809500736505629
11439273457043778342886520414406889931665405902225381466032093468840906182958490
40067007538064972385604595988992584428764922065891385936525652108407127397334152
77577987012630748507156907484796296338436704249630828087630933181681675714232538
16498780846317942261636333677867775907817110548388045747416843463971305692382555
89878879946652085040124400663361621041802303798851312902275116583020189066763097
91689409840039779256377571669958449000393642378255399242442113685637190833646231
60095781323920529612304032574006289858849152651185411400724689927483641929978732
84494632525395498991320929552989623300263691567683124365569447208054714159350718
63081591475681297110348362690241888609141200075894224902233777226934570198564942
62389139828831041708163976489496312464200707570741034016781184280864313770936734
13966676078625322832980760007005101208004862489022724360915838409882820115703865
87664513792449357865428353820641409325285645926908332041329290558106564944174076
98448004396467710266573872541626995999259033200592474025071939364137062868568245
40734756324139507980786753851729563208385832156894416773034762738098032723080792
44765548714573368685710420592114171685959409873882010592984896309489553137529668
91261773670019374523853883132878624380432154218001861595424734463034850618569271
90374542447907623411522862507612119581666719929400829186215604902943767120983212
02544471505640473874642620655839478699640912521556176910985261615442047526176536
97235141776796603138771014504394692811314927335598670398583505556436746592441767
65771005964615976778954300093716097017729308511008961468237442731938425891977201
43587672002741894902489811053845671227067431958118922138122251574506953505558995
19258472626056397456785349396054603988694509052100709605927409090593842025207354
53313462229692876098074439774957481872935471486966758326990650144914948701503314
13597050267262313782154363746508982901493908456173011366863634889451170372971355
04297785158228883263267504636547471231970192746858267907181665167734883778841765
83973988012795466494399915908765156011779802198725955892514094797030806615012236
41998374589083393493611728331296674043069686059246210768061588107388102747930427
68451858240898452894051575403248691748327785429480763732692852643167679903876653
82998798035791949083798194517073558852275583546403172355538414465285125901769060
33520956224233729387609101357489339640000565938555353265179358602368647929443736
50077755675142991038654325122351616097963840061225439309347683450344473435729237
54729823959690842761271167222679111659279222369969505862293059483051588848405865
434231536619097363136237839395246042763998493353068767
*and by "eventually", i mean less than 5 seconds on my laptop..
Yes it is a limitation of PHP and there is nothing you can do about it short of recompiling your PHP interpreter. Even then you are limited to the types your native system supports which wouldn't be bigger than 64bit normally. You can, as you know, use GMP, or BCMath, but that is not what your asking.
Under the hood, depending on your system the PHP integer and PHP floating point types corresponds to a signed C integer type, and C float types (PHP always uses C doubles for 'floats' AFAIK). This is a static relationship and can't change after compile time. Since the C types have fixed precision of course the PHP ones do too.
The "overflow" into a float is just a convenient compromise so you can store really big numbers, rather than not at all. Your losing some precision yeah, but only in the significand. PHP is not going to automatically convert the number to some other bigger precision floating point format because it doesn't have one.
I was trying to do some sort of XOR binary crypting algorithm in PHP and so I needed to convert large strings into binary. The problem is that PHP seems to be very limited in terms of binary calculation / storage as a string of six letters only, once converted, exceeds the PHP INT limit.
That means unpacking a big string to binary just gives a unusable number. I tried to do the string unpacking by splitting the string into packs of 4 letters and then unpacking them, but then I've got troubles with the repacking where it gives random characters instead of the original ones.
How can I do the unpacking of very long strings, and then store them either in a string (made only of 0s and 1s) or in a big array (where each value is either a 0 or 1, the key indicating the location of this bit) ?
have you tried the GMP library? Man page GMP
quick test code:
<?php
$gmpValue1 = gmp_init("1562767628166296698262", 10); // note: using base 10 (decimal)
$gmpValue2 = gmp_init("2163623626362663286446", 10);
$gmpValue3 = gmp_xor($gmpValue1, $gmpValue2);
echo gmp_strval($gmpValue3, 10) . "\n"; // note: using base 10 (decimal)
I want to cmmunicate with a udp tracker and send a connect request to it.
for this i must at first create a 16-bytes variable in php and sends it to the tracker.
request variable structure:
put it first 64-bit 0x41727101980 and 32-bit with 0.
generate a 32-bit unsigned integer as random and then add generated number to end of the variable.
finally, the size of my variable should be 128-bit equal to 16-bytes.
I use this way:
$a = time();
$connect_request = 0x41727101980;
$connect_request .= 0x00000000;
$connect_request .= $a;
var_dump($connect_request);
but result is:
string '449748612544001413719239' (length=24)
this is 24-byte not 16-byte.
what I must do now ??
What you have there are numbers.
Try this:
$connect_request = pack("N*",0x417,0x27101980,0,time());
If you have PHP 5.6 then you can use pack("JN*", 0x41727101980, 0, time()), allowing for direct 64-bit number use, but otherwise you will need to manually split the big number into two smaller numbers (each 32-bit, hence the second 0x... having eight hexits).
This will pack the inputs into a string of your desired length, in this case 128-bit, or 24 bytes.
I've used unpack to convert most of the data types I have in a binary file that I'm parsing with little problems. I have no idea how to work with a big endian 64-bit signed long. I think this data type is stored using 2's complement. The application of the data file I'm reading is a java app so I assume it's 2's complement. I don't need to work with it as a number but simply work with it as a string.
Java 64-bit integers are indeed stored natively as "network-order" (big endian, i.e. start with the most significant byte) 8-byte 2's complement format. So typically you take byte at a time, shift left by 8, repeat. Byte values can be thought of as unsigned (while result is signed), but with left-shifting this should not matter.
So: first you just created equivalent 64-bit int from bytes, and display from there. No point in using short cuts; while it is possible, you just end up with more complicated and less efficient code.
32-bit PHP will only have signed 32-bit integers, thus as far as I know, there's no way to natively unpack the data.
The following code should be able to read a big endian, two's complement 64-bit integer:
<?php
function read_int64($fp)
{
$hex = unpack('H16a', fread($fp, 8));
$hex = '0x'.$hex['a'];
$n = gmp_init($hex);
if (gmp_testbit($n, 63))
{
$n = gmp_xor($n, '0xffffffffffffffff'); // flip the bits
$n = gmp_neg(gmp_add($n, 1)); // add one and negate
}
return gmp_strval($n);
}
?>
It returns the integer as a string. It can be used like:
$fp = fopen('test.bin', 'rb');
echo read_int64($fp)."\n";
fclose($fp);
(Edit: Updated code to call fewer GMP functions.)