-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathByteFormatter.php
More file actions
321 lines (273 loc) · 7.85 KB
/
ByteFormatter.php
File metadata and controls
321 lines (273 loc) · 7.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
<?php
namespace ScriptFUSION\Byte;
use ScriptFUSION\Byte\Unit\SymbolDecorator;
use ScriptFUSION\Byte\Unit\UnitDecorator;
/**
* Formats byte values as human-readable strings.
*/
class ByteFormatter
{
private int $base = Base::BINARY;
private string $format;
private string $sprintfFormat;
private int $precision = 0;
private bool $automaticPrecision = true;
private ?int $exponent = null;
private ?int $significantFigures = null;
private UnitDecorator $unitDecorator;
/**
* Initializes this instance, optionally with a specific unit decorator.
* If no unit decorator is specified, SymbolDecorator will be used.
*
* @param UnitDecorator|null $unitDecorator Optional. Unit decorator.
*/
public function __construct(?UnitDecorator $unitDecorator = null)
{
$this
->setUnitDecorator($unitDecorator ?: new SymbolDecorator)
->setFormat('%v %u')
;
}
/**
* Formats the specified number of bytes as a human-readable string.
*
* @param int|float $bytes Number of bytes.
* @param int|null $precision Optional. Number of fractional digits.
*
* @return string Formatted bytes.
*/
public function format(int|float $bytes, ?int $precision = null): string
{
// Use default precision when not specified.
$precision === null && $precision = $this->getPrecision();
$log = log($bytes, $this->getBase());
$exponent = $this->hasFixedExponent() ? $this->getFixedExponent() : max(0, $log > -INF ? (int)$log : 0);
$rawValue = $this->getBase() ** ($log - $exponent);
$value = $this->significantFigures ? $this->roundToSignificantFigures($rawValue) : round($rawValue, $precision);
$units = $this->getUnitDecorator()->decorate($exponent, $this->getBase(), $value);
return trim(sprintf($this->sprintfFormat, $this->formatValue($value, $precision), $units));
}
/**
* Rounds the specified value to the number of significant figures defined for this instance.
*
* @param float $value Value.
*
* @return float Rounded value.
*/
private function roundToSignificantFigures(float $value): float
{
if ($value === 0.) {
return 0.;
}
$factor = 10 ** ($this->significantFigures - floor(log10(abs($value))) - 1);
return round($value * $factor) / $factor;
}
/**
* Formats the specified number with the specified precision or significant figures.
*
* If precision scaling is enabled, the precision may be reduced when it
* contains insignificant digits. If the fractional part is zero, it will
* be completely removed.
*
* @param float $value Number.
* @param int $precision Number of fractional digits. Ignored when significant figures are set.
*
* @return string Formatted number.
*/
private function formatValue(float $value, int $precision): string
{
if ($this->significantFigures !== null) {
return (string)$value;
}
$formatted = sprintf("%0.{$precision}F", $value);
if ($this->hasAutomaticPrecision()) {
// [0 => integer part, 1 => fractional part].
$formattedParts = explode('.', $formatted);
if (isset($formattedParts[1])) {
// Strip trailing 0s in fractional part.
if (!$formattedParts[1] = rtrim($formattedParts[1], '0')) {
// Remove fractional part.
unset($formattedParts[1]);
}
$formatted = implode('.', $formattedParts);
}
}
return $formatted;
}
/**
* Coverts a format specifier into a sprintf() compatible format.
*
* @param string $format Format specifier.
*
* @return string sprintf() format.
*/
private function convertFormat(string $format): string
{
return str_replace(['%v', '%u'], ['%1$s', '%2$s'], $format);
}
/**
* Gets the exponentiation base.
*
* @return int Exponentiation base.
*/
public function getBase(): int
{
return $this->base;
}
/**
* Sets the exponentiation base, which should usually be a Base constant.
*
* @param int $base Exponentiation base.
*
* @return $this
*/
public function setBase(int $base): self
{
$this->base = $base;
return $this;
}
/**
* Gets the format specifier.
*
* @return string Format specifier.
*/
public function getFormat(): string
{
return $this->format;
}
/**
* Sets the format specifier. Occurrences of %v and %u will be replaced
* with formatted byte values and units, respectively.
*
* @param string $format Format specifier.
*
* @return $this
*/
public function setFormat(string $format): self
{
$this->sprintfFormat = $this->convertFormat($this->format = $format);
return $this;
}
/**
* Gets the maximum number of fractional digits.
*
* @return int Fractional digits.
*/
public function getPrecision(): int
{
return $this->precision;
}
/**
* Sets the maximum number of fractional digits.
*
* @param int $precision Fractional digits.
*
* @return $this
*/
public function setPrecision(int $precision): self
{
$this->precision = $precision;
$this->significantFigures = null;
return $this;
}
/**
* Enables automatic precision scaling.
*
* @return $this
*/
public function enableAutomaticPrecision(): self
{
$this->automaticPrecision = true;
return $this;
}
/**
* Disables automatic precision scaling.
*
* @return $this
*/
public function disableAutomaticPrecision(): self
{
$this->automaticPrecision = false;
return $this;
}
/**
* Gets a value indicating whether precision will be scaled automatically.
*
* @return bool True if precision is scaled automatically, otherwise false.
*/
public function hasAutomaticPrecision(): bool
{
return $this->automaticPrecision;
}
/**
* Gets the fixed exponent.
*
* @return int Fixed exponent.
*/
public function getFixedExponent(): int
{
return $this->exponent;
}
/**
* Sets the fixed exponent.
*
* @param int $exponent Fixed exponent.
*
* @return $this
*/
public function setFixedExponent(int $exponent): self
{
$this->exponent = $exponent;
return $this;
}
/**
* Clears any fixed exponent.
*
* @return $this
*/
public function clearFixedExponent(): self
{
$this->exponent = null;
return $this;
}
/**
* Gets a value indicating whether a fixed exponent has been set.
*
* @return bool True if a fixed exponent has been set, otherwise false.
*/
public function hasFixedExponent(): bool
{
return $this->exponent !== null;
}
/**
* Sets the number of significant figures.
*
* @param int $significantFigures Number of significant figures.
*/
public function setSignificantFigures(int $significantFigures): self
{
$this->significantFigures = $significantFigures;
return $this;
}
/**
* Gets the unit decorator.
*
* @return UnitDecorator
*/
public function getUnitDecorator(): UnitDecorator
{
return $this->unitDecorator;
}
/**
* Sets the unit decorator.
*
* @param UnitDecorator $decorator Unit decorator.
*
* @return $this
*/
public function setUnitDecorator(UnitDecorator $decorator): self
{
$this->unitDecorator = $decorator;
return $this;
}
}