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 = [
8989const LOG_2_1024 = Math . log ( 1024 ) ;
9090const 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 ;
399472exports . filesize = filesize ;
473+ exports . getBaseConfiguration = getBaseConfiguration ;
474+ exports . handleZeroValue = handleZeroValue ;
400475exports . partial = partial ;
0 commit comments