index.js
1 /*! 2 * bytes 3 * Copyright(c) 2012-2014 TJ Holowaychuk 4 * Copyright(c) 2015 Jed Watson 5 * MIT Licensed 6 */ 7 8 'use strict'; 9 10 /** 11 * Module exports. 12 * @public 13 */ 14 15 module.exports = bytes; 16 module.exports.format = format; 17 module.exports.parse = parse; 18 19 /** 20 * Module variables. 21 * @private 22 */ 23 24 var formatThousandsRegExp = /\B(?=(\d{3})+(?!\d))/g; 25 26 var formatDecimalsRegExp = /(?:\.0*|(\.[^0]+)0+)$/; 27 28 var map = { 29 b: 1, 30 kb: 1 << 10, 31 mb: 1 << 20, 32 gb: 1 << 30, 33 tb: Math.pow(1024, 4), 34 pb: Math.pow(1024, 5), 35 }; 36 37 var parseRegExp = /^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb|pb)$/i; 38 39 /** 40 * Convert the given value in bytes into a string or parse to string to an integer in bytes. 41 * 42 * @param {string|number} value 43 * @param {{ 44 * case: [string], 45 * decimalPlaces: [number] 46 * fixedDecimals: [boolean] 47 * thousandsSeparator: [string] 48 * unitSeparator: [string] 49 * }} [options] bytes options. 50 * 51 * @returns {string|number|null} 52 */ 53 54 function bytes(value, options) { 55 if (typeof value === 'string') { 56 return parse(value); 57 } 58 59 if (typeof value === 'number') { 60 return format(value, options); 61 } 62 63 return null; 64 } 65 66 /** 67 * Format the given value in bytes into a string. 68 * 69 * If the value is negative, it is kept as such. If it is a float, 70 * it is rounded. 71 * 72 * @param {number} value 73 * @param {object} [options] 74 * @param {number} [options.decimalPlaces=2] 75 * @param {number} [options.fixedDecimals=false] 76 * @param {string} [options.thousandsSeparator=] 77 * @param {string} [options.unit=] 78 * @param {string} [options.unitSeparator=] 79 * 80 * @returns {string|null} 81 * @public 82 */ 83 84 function format(value, options) { 85 if (!Number.isFinite(value)) { 86 return null; 87 } 88 89 var mag = Math.abs(value); 90 var thousandsSeparator = (options && options.thousandsSeparator) || ''; 91 var unitSeparator = (options && options.unitSeparator) || ''; 92 var decimalPlaces = (options && options.decimalPlaces !== undefined) ? options.decimalPlaces : 2; 93 var fixedDecimals = Boolean(options && options.fixedDecimals); 94 var unit = (options && options.unit) || ''; 95 96 if (!unit || !map[unit.toLowerCase()]) { 97 if (mag >= map.pb) { 98 unit = 'PB'; 99 } else if (mag >= map.tb) { 100 unit = 'TB'; 101 } else if (mag >= map.gb) { 102 unit = 'GB'; 103 } else if (mag >= map.mb) { 104 unit = 'MB'; 105 } else if (mag >= map.kb) { 106 unit = 'KB'; 107 } else { 108 unit = 'B'; 109 } 110 } 111 112 var val = value / map[unit.toLowerCase()]; 113 var str = val.toFixed(decimalPlaces); 114 115 if (!fixedDecimals) { 116 str = str.replace(formatDecimalsRegExp, '$1'); 117 } 118 119 if (thousandsSeparator) { 120 str = str.split('.').map(function (s, i) { 121 return i === 0 122 ? s.replace(formatThousandsRegExp, thousandsSeparator) 123 : s 124 }).join('.'); 125 } 126 127 return str + unitSeparator + unit; 128 } 129 130 /** 131 * Parse the string value into an integer in bytes. 132 * 133 * If no unit is given, it is assumed the value is in bytes. 134 * 135 * @param {number|string} val 136 * 137 * @returns {number|null} 138 * @public 139 */ 140 141 function parse(val) { 142 if (typeof val === 'number' && !isNaN(val)) { 143 return val; 144 } 145 146 if (typeof val !== 'string') { 147 return null; 148 } 149 150 // Test if the string passed is valid 151 var results = parseRegExp.exec(val); 152 var floatValue; 153 var unit = 'b'; 154 155 if (!results) { 156 // Nothing could be extracted from the given string 157 floatValue = parseInt(val, 10); 158 unit = 'b' 159 } else { 160 // Retrieve the value and the unit 161 floatValue = parseFloat(results[1]); 162 unit = results[4].toLowerCase(); 163 } 164 165 if (isNaN(floatValue)) { 166 return null; 167 } 168 169 return Math.floor(map[unit] * floatValue); 170 }