Skip to content

Commit 6a2502a

Browse files
committed
Reshaping the code for better testing
1 parent cc9e109 commit 6a2502a

File tree

11 files changed

+917
-432
lines changed

11 files changed

+917
-432
lines changed

dist/filesize.cjs

Lines changed: 180 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* @copyright 2025 Jason Mulligan <jason.mulligan@avoidwork.com>
55
* @license BSD-3-Clause
6-
* @version 11.0.10
6+
* @version 11.0.11
77
*/
88
'use strict';
99

@@ -89,6 +89,13 @@ const DECIMAL_POWERS = [
8989
const LOG_2_1024 = Math.log(1024);
9090
const LOG_10_1000 = Math.log(1000);
9191

92+
// Cached configuration lookup for better performance
93+
const STANDARD_CONFIGS = {
94+
[SI]: { isDecimal: true, ceil: 1000, actualStandard: JEDEC },
95+
[IEC]: { isDecimal: false, ceil: 1024, actualStandard: IEC },
96+
[JEDEC]: { isDecimal: false, ceil: 1024, actualStandard: JEDEC }
97+
};
98+
9299
/**
93100
* Converts a file size in bytes to a human-readable string with appropriate units
94101
* @param {number|string|bigint} arg - The file size in bytes to convert
@@ -140,29 +147,9 @@ function filesize (arg, {
140147
val = 0,
141148
u = EMPTY;
142149

143-
// Optimized base & standard synchronization with early returns
144-
let isDecimal, ceil, actualStandard;
145-
if (standard === SI) {
146-
isDecimal = true;
147-
ceil = 1000;
148-
actualStandard = JEDEC;
149-
} else if (standard === IEC) {
150-
isDecimal = false;
151-
ceil = 1024;
152-
actualStandard = IEC;
153-
} else if (standard === JEDEC) {
154-
isDecimal = false; // JEDEC uses binary (1024) by default
155-
ceil = 1024;
156-
actualStandard = JEDEC;
157-
} else if (base === 2) {
158-
isDecimal = false;
159-
ceil = 1024;
160-
actualStandard = IEC;
161-
} else {
162-
isDecimal = true;
163-
ceil = 1000;
164-
actualStandard = JEDEC;
165-
}
150+
// Optimized base & standard configuration lookup
151+
const config = getBaseConfiguration(standard, base);
152+
const { isDecimal, ceil, actualStandard } = config;
166153

167154
const full = fullform === true,
168155
neg = num < 0,
@@ -183,38 +170,12 @@ function filesize (arg, {
183170

184171
// Fast path for zero
185172
if (num === 0) {
186-
result[0] = precision > 0 ? (0).toPrecision(precision) : 0;
187-
u = result[1] = STRINGS.symbol[actualStandard][bits ? BITS : BYTES][0];
188-
189-
if (output === EXPONENT) {
190-
return 0;
191-
}
192-
193-
// Skip most processing for zero case
194-
if (symbols[result[1]]) {
195-
result[1] = symbols[result[1]];
196-
}
197-
198-
if (full) {
199-
result[1] = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE);
200-
}
201-
202-
return output === ARRAY ? result : output === OBJECT ? {
203-
value: result[0],
204-
symbol: result[1],
205-
exponent: 0,
206-
unit: u
207-
} : result.join(spacer);
173+
return handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer);
208174
}
209175

210176
// Optimized exponent calculation using pre-computed log values
211177
if (e === -1 || isNaN(e)) {
212-
if (isDecimal) {
213-
e = Math.floor(Math.log(num) / LOG_10_1000);
214-
} else {
215-
e = Math.floor(Math.log(num) / LOG_2_1024);
216-
}
217-
178+
e = isDecimal ? Math.floor(Math.log(num) / LOG_10_1000) : Math.floor(Math.log(num) / LOG_2_1024);
218179
if (e < 0) {
219180
e = 0;
220181
}
@@ -232,24 +193,10 @@ function filesize (arg, {
232193
return e;
233194
}
234195

235-
// Use pre-computed lookup tables (e is always <= 8, arrays have 9 elements)
236-
let d;
237-
if (isDecimal) {
238-
d = DECIMAL_POWERS[e];
239-
} else {
240-
d = BINARY_POWERS[e];
241-
}
242-
243-
val = num / d;
244-
245-
if (bits) {
246-
val = val * 8;
247-
248-
if (val >= ceil && e < 8) {
249-
val = val / ceil;
250-
e++;
251-
}
252-
}
196+
// Calculate value with optimized lookup and bits handling
197+
const valueResult = calculateOptimizedValue(num, e, isDecimal, bits, ceil);
198+
val = valueResult.val;
199+
e = valueResult.e;
253200

254201
// Optimize rounding calculation
255202
const p = e > 0 && round > 0 ? Math.pow(10, round) : 1;
@@ -260,21 +207,11 @@ function filesize (arg, {
260207
e++;
261208
}
262209

263-
// Setting optional precision
210+
// Apply precision handling
264211
if (precision > 0) {
265-
result[0] = result[0].toPrecision(precision);
266-
267-
if (result[0].includes(E) && e < 8) {
268-
e++;
269-
// Recalculate with new exponent (e is always <= 8)
270-
if (isDecimal) {
271-
d = DECIMAL_POWERS[e];
272-
} else {
273-
d = BINARY_POWERS[e];
274-
}
275-
val = num / d;
276-
result[0] = (p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p).toPrecision(precision);
277-
}
212+
const precisionResult = applyPrecisionHandling(result[0], precision, e, num, isDecimal, bits, ceil, roundingFunc, round);
213+
result[0] = precisionResult.value;
214+
e = precisionResult.e;
278215
}
279216

280217
// Cache symbol lookup
@@ -291,25 +228,8 @@ function filesize (arg, {
291228
result[1] = symbols[result[1]];
292229
}
293230

294-
// Optimized locale/separator handling
295-
if (locale === true) {
296-
result[0] = result[0].toLocaleString();
297-
} else if (locale.length > 0) {
298-
result[0] = result[0].toLocaleString(locale, localeOptions);
299-
} else if (separator.length > 0) {
300-
result[0] = result[0].toString().replace(PERIOD, separator);
301-
}
302-
303-
if (pad && round > 0) {
304-
const resultStr = result[0].toString(),
305-
x = separator || ((resultStr.match(/(\D)/g) || []).pop() || PERIOD),
306-
tmp = resultStr.split(x),
307-
s = tmp[1] || EMPTY,
308-
l = s.length,
309-
n = round - l;
310-
311-
result[0] = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`;
312-
}
231+
// Apply locale, separator, and padding formatting
232+
result[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round);
313233

314234
if (full) {
315235
result[1] = fullforms[e] || STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S);
@@ -319,7 +239,7 @@ function filesize (arg, {
319239
if (output === ARRAY) {
320240
return result;
321241
}
322-
242+
323243
if (output === OBJECT) {
324244
return {
325245
value: result[0],
@@ -328,10 +248,160 @@ function filesize (arg, {
328248
unit: u
329249
};
330250
}
331-
251+
332252
return spacer === SPACE ? `${result[0]} ${result[1]}` : result.join(spacer);
333253
}
334254

255+
/**
256+
* Optimized base configuration lookup
257+
* @param {string} standard - Standard type
258+
* @param {number} base - Base number
259+
* @returns {Object} Configuration object
260+
*/
261+
function getBaseConfiguration(standard, base) {
262+
// Use cached lookup table for better performance
263+
if (STANDARD_CONFIGS[standard]) {
264+
return STANDARD_CONFIGS[standard];
265+
}
266+
267+
// Base override
268+
if (base === 2) {
269+
return { isDecimal: false, ceil: 1024, actualStandard: IEC };
270+
}
271+
272+
// Default
273+
return { isDecimal: true, ceil: 1000, actualStandard: JEDEC };
274+
}
275+
276+
/**
277+
* Optimized zero value handling
278+
* @param {number} precision - Precision value
279+
* @param {string} actualStandard - Standard to use
280+
* @param {boolean} bits - Whether to use bits
281+
* @param {Object} symbols - Custom symbols
282+
* @param {boolean} full - Whether to use full form
283+
* @param {Array} fullforms - Custom full forms
284+
* @param {string} output - Output format
285+
* @param {string} spacer - Spacer character
286+
* @returns {string|Array|Object|number} Formatted result
287+
*/
288+
function handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer) {
289+
const result = [];
290+
result[0] = precision > 0 ? (0).toPrecision(precision) : 0;
291+
const u = result[1] = STRINGS.symbol[actualStandard][bits ? BITS : BYTES][0];
292+
293+
if (output === EXPONENT) {
294+
return 0;
295+
}
296+
297+
// Apply symbol customization
298+
if (symbols[result[1]]) {
299+
result[1] = symbols[result[1]];
300+
}
301+
302+
// Apply full form
303+
if (full) {
304+
result[1] = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE);
305+
}
306+
307+
// Return in requested format
308+
return output === ARRAY ? result : output === OBJECT ? {
309+
value: result[0],
310+
symbol: result[1],
311+
exponent: 0,
312+
unit: u
313+
} : result.join(spacer);
314+
}
315+
316+
/**
317+
* Optimized value calculation with bits handling
318+
* @param {number} num - Input number
319+
* @param {number} e - Exponent
320+
* @param {boolean} isDecimal - Whether to use decimal powers
321+
* @param {boolean} bits - Whether to calculate bits
322+
* @param {number} ceil - Ceiling value for auto-increment
323+
* @returns {Object} Object with val and e properties
324+
*/
325+
function calculateOptimizedValue(num, e, isDecimal, bits, ceil) {
326+
const d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e];
327+
let val = num / d;
328+
329+
if (bits) {
330+
val *= 8;
331+
// Handle auto-increment for bits
332+
if (val >= ceil && e < 8) {
333+
val /= ceil;
334+
e++;
335+
}
336+
}
337+
338+
return { val, e };
339+
}
340+
341+
/**
342+
* Optimized precision handling with scientific notation correction
343+
* @param {number} value - Current value
344+
* @param {number} precision - Precision to apply
345+
* @param {number} e - Current exponent
346+
* @param {number} num - Original number
347+
* @param {boolean} isDecimal - Whether using decimal base
348+
* @param {boolean} bits - Whether calculating bits
349+
* @param {number} ceil - Ceiling value
350+
* @param {Function} roundingFunc - Rounding function
351+
* @param {number} round - Round value
352+
* @returns {Object} Object with value and e properties
353+
*/
354+
function applyPrecisionHandling(value, precision, e, num, isDecimal, bits, ceil, roundingFunc, round) {
355+
let result = value.toPrecision(precision);
356+
357+
// Handle scientific notation by recalculating with incremented exponent
358+
if (result.includes(E) && e < 8) {
359+
e++;
360+
const valueResult = calculateOptimizedValue(num, e, isDecimal, bits, ceil);
361+
const p = round > 0 ? Math.pow(10, round) : 1;
362+
result = (p === 1 ? roundingFunc(valueResult.val) : roundingFunc(valueResult.val * p) / p).toPrecision(precision);
363+
}
364+
365+
return { value: result, e };
366+
}
367+
368+
/**
369+
* Optimized number formatting with locale, separator, and padding
370+
* @param {number|string} value - Value to format
371+
* @param {string|boolean} locale - Locale setting
372+
* @param {Object} localeOptions - Locale options
373+
* @param {string} separator - Custom separator
374+
* @param {boolean} pad - Whether to pad
375+
* @param {number} round - Round value
376+
* @returns {string|number} Formatted value
377+
*/
378+
function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) {
379+
let result = value;
380+
381+
// Apply locale formatting
382+
if (locale === true) {
383+
result = result.toLocaleString();
384+
} else if (locale.length > 0) {
385+
result = result.toLocaleString(locale, localeOptions);
386+
} else if (separator.length > 0) {
387+
result = result.toString().replace(PERIOD, separator);
388+
}
389+
390+
// Apply padding
391+
if (pad && round > 0) {
392+
const resultStr = result.toString();
393+
const x = separator || ((resultStr.match(/(\D)/g) || []).pop() || PERIOD);
394+
const tmp = resultStr.split(x);
395+
const s = tmp[1] || EMPTY;
396+
const l = s.length;
397+
const n = round - l;
398+
399+
result = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`;
400+
}
401+
402+
return result;
403+
}
404+
335405
/**
336406
* Creates a partially applied version of filesize with preset options
337407
* @param {Object} [options={}] - Default options to apply to the returned function
@@ -396,5 +466,10 @@ function partial ({
396466
});
397467
}
398468

469+
exports.applyNumberFormatting = applyNumberFormatting;
470+
exports.applyPrecisionHandling = applyPrecisionHandling;
471+
exports.calculateOptimizedValue = calculateOptimizedValue;
399472
exports.filesize = filesize;
473+
exports.getBaseConfiguration = getBaseConfiguration;
474+
exports.handleZeroValue = handleZeroValue;
400475
exports.partial = partial;

0 commit comments

Comments
 (0)