index.js (2900B)
1 /*! 2 * range-parser 3 * Copyright(c) 2012-2014 TJ Holowaychuk 4 * Copyright(c) 2015-2016 Douglas Christopher Wilson 5 * MIT Licensed 6 */ 7 8 'use strict' 9 10 /** 11 * Module exports. 12 * @public 13 */ 14 15 module.exports = rangeParser 16 17 /** 18 * Parse "Range" header `str` relative to the given file `size`. 19 * 20 * @param {Number} size 21 * @param {String} str 22 * @param {Object} [options] 23 * @return {Array} 24 * @public 25 */ 26 27 function rangeParser (size, str, options) { 28 if (typeof str !== 'string') { 29 throw new TypeError('argument str must be a string') 30 } 31 32 var index = str.indexOf('=') 33 34 if (index === -1) { 35 return -2 36 } 37 38 // split the range string 39 var arr = str.slice(index + 1).split(',') 40 var ranges = [] 41 42 // add ranges type 43 ranges.type = str.slice(0, index) 44 45 // parse all ranges 46 for (var i = 0; i < arr.length; i++) { 47 var range = arr[i].split('-') 48 var start = parseInt(range[0], 10) 49 var end = parseInt(range[1], 10) 50 51 // -nnn 52 if (isNaN(start)) { 53 start = size - end 54 end = size - 1 55 // nnn- 56 } else if (isNaN(end)) { 57 end = size - 1 58 } 59 60 // limit last-byte-pos to current length 61 if (end > size - 1) { 62 end = size - 1 63 } 64 65 // invalid or unsatisifiable 66 if (isNaN(start) || isNaN(end) || start > end || start < 0) { 67 continue 68 } 69 70 // add range 71 ranges.push({ 72 start: start, 73 end: end 74 }) 75 } 76 77 if (ranges.length < 1) { 78 // unsatisifiable 79 return -1 80 } 81 82 return options && options.combine 83 ? combineRanges(ranges) 84 : ranges 85 } 86 87 /** 88 * Combine overlapping & adjacent ranges. 89 * @private 90 */ 91 92 function combineRanges (ranges) { 93 var ordered = ranges.map(mapWithIndex).sort(sortByRangeStart) 94 95 for (var j = 0, i = 1; i < ordered.length; i++) { 96 var range = ordered[i] 97 var current = ordered[j] 98 99 if (range.start > current.end + 1) { 100 // next range 101 ordered[++j] = range 102 } else if (range.end > current.end) { 103 // extend range 104 current.end = range.end 105 current.index = Math.min(current.index, range.index) 106 } 107 } 108 109 // trim ordered array 110 ordered.length = j + 1 111 112 // generate combined range 113 var combined = ordered.sort(sortByRangeIndex).map(mapWithoutIndex) 114 115 // copy ranges type 116 combined.type = ranges.type 117 118 return combined 119 } 120 121 /** 122 * Map function to add index value to ranges. 123 * @private 124 */ 125 126 function mapWithIndex (range, index) { 127 return { 128 start: range.start, 129 end: range.end, 130 index: index 131 } 132 } 133 134 /** 135 * Map function to remove index value from ranges. 136 * @private 137 */ 138 139 function mapWithoutIndex (range) { 140 return { 141 start: range.start, 142 end: range.end 143 } 144 } 145 146 /** 147 * Sort function to sort ranges by index. 148 * @private 149 */ 150 151 function sortByRangeIndex (a, b) { 152 return a.index - b.index 153 } 154 155 /** 156 * Sort function to sort ranges by start position. 157 * @private 158 */ 159 160 function sortByRangeStart (a, b) { 161 return a.start - b.start 162 }