[BUG] Přetečení v TemplateHelpers::Bytes
- DocX
- Člen | 154
Ahoj,
narazil jsem na chybku v template helperu bytes (TemplateHelpers.php 213 2009–02–13 18:44:46Z). Když předávám číslo, které se nevejde do intu (něco přes 2G) tak to přeteče a výsledkem je něco hnusného :)
U sebe jsem to vyřešil úpravou kódu z
<?php
public static function bytes($bytes) {
$bytes = (int) $bytes;
...
?>
na
<?php
public static function bytes($bytes) {
$bytes = round((float) $bytes, 0);
...
?>
Je to asi o něco náročnější, ale jinak ta funkce nedává smysl (hlavně ty předpony TB a PT) :).
Editoval DocX (2. 5. 2009 11:19)
- PetrP
- Člen | 587
Hmmm mě to ‚přetejka‘ až při PB.
$b = 1;Debug::timer('bytestest');
for($i=0;$i<10;$i++)
{
d(TemplateHelpers::bytes($b),$b);
$b*=1024;
}
d(Debug::timer('bytestest'));
// momentální stav
array(2) {
0 => string(3) "1 B"
1 => int(1)
}
array(2) {
0 => string(4) "1 kB"
1 => int(1024)
}
array(2) {
0 => string(4) "1 MB"
1 => int(1048576)
}
array(2) {
0 => string(4) "1 GB"
1 => int(1073741824)
}
array(2) {
0 => string(4) "1 TB"
1 => int(1099511627776)
}
array(2) {
0 => string(4) "1 PB"
1 => int(1125899906842624)
}
array(2) {
0 => string(4) "1 PB"
1 => int(1152921504606846976)
}
array(2) {
0 => string(3) "0 B"
1 => float(1.1805916207174E+21)
}
array(2) {
0 => string(3) "0 B"
1 => float(1.2089258196146E+24)
}
array(2) {
0 => string(3) "0 B"
1 => float(1.2379400392854E+27)
}
float(0.00072598457336426)
// další časy
float(0.00074386596679688)
float(0.00075221061706543)
float(0.00074505805969238)
float(0.00078892707824707)
float(0.00074315071105957)
// po přepsání $bytes = round((float) $bytes, 0);
array(2) {
0 => string(3) "1 B"
1 => int(1)
}
array(2) {
0 => string(4) "1 kB"
1 => int(1024)
}
array(2) {
0 => string(4) "1 MB"
1 => int(1048576)
}
array(2) {
0 => string(4) "1 GB"
1 => int(1073741824)
}
array(2) {
0 => string(4) "1 TB"
1 => int(1099511627776)
}
array(2) {
0 => string(4) "1 PB"
1 => int(1125899906842624)
}
array(2) {
0 => string(4) "1 PB"
1 => int(1152921504606846976)
}
array(2) {
0 => string(7) "1024 PB"
1 => float(1.1805916207174E+21)
}
array(2) {
0 => string(10) "1048576 PB"
1 => float(1.2089258196146E+24)
}
array(2) {
0 => string(13) "1073741824 PB"
1 => float(1.2379400392854E+27)
}
float(0.00075197219848633)
// další časy
float(0.00076603889465332)
float(0.00076198577880859)
float(0.00075793266296387)
float(0.00076508522033691)
float(0.00075292587280273)
Takže jestli je nějaké zpomalení tak je nemeřitelné.
Nicméně je tam nějaká chybka ještě protože 1152921504606846976 = 1024PB a ne 1PB
Editoval PetrP (15. 5. 2009 10:17)
- DocX
- Člen | 154
Na začátek díky za podrobné změření.
PetrP napsal(a):
Hmmm mě to ‚přetejka‘ až při PB.
Většímu intu zřejmě vděčíš 64-bitovému systému.
Nicméně je tam nějaká chybka ještě protože 1152921504606846976 = 1024PB a ne 1PB
Co se týče chyby s PB, tak jde o ten poslední krok, kdy je v $unit PB, kde $bytes zmenší o řád a čeká na další předponu, která to vykompenzuje. Ta už ale není.
Mám tedy takovýto návrh:
<?php
public static function bytes($bytes)
{
// v trunku už je round, ale asi bych tam nechal
// i přetypování, když by tam někdo vkládal něco jiného
$bytes = round((float)$bytes);
$units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB');
foreach ($units as $unit) {
if (abs($bytes) < 1024 || $unit == 'PB') break;
$bytes = $bytes / 1024;
}
return round($bytes, 2) . ' ' . $unit;
}
?>
- DocX
- Člen | 154
Až teď jsem si všiml, že je tam stále problém s následujícím:
<?php
echo TemplateHelpers::bytes(pow(1024,5)) // 1PB
echo TemplateHelpers::bytes(pow(1024,6)) // 1PB (správně 1024PB)
echo TemplateHelpers::bytes(pow(1024,7)) // 1024PB (správně 1048576PB)
?>
řešení:
<?php
public static function bytes($bytes, $precision = 2)
{
$bytes = round($bytes);
$units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB');
foreach ($units as $unit) {
- if (abs($bytes) < 1024) break;
+ if (abs($bytes) < 1024 || $unit == 'PB') break;
$bytes = $bytes / 1024;
}
return round($bytes, $precision) . ' ' . $unit;
}
?>
Je to sice okrajová funkce, ale chyba může způsobit docela velké problémy :)
Editoval DocX (18. 8. 2009 14:08)