You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
2.8 KiB
162 lines
2.8 KiB
/*! |
|
* range-parser |
|
* Copyright(c) 2012-2014 TJ Holowaychuk |
|
* Copyright(c) 2015-2016 Douglas Christopher Wilson |
|
* MIT Licensed |
|
*/ |
|
|
|
'use strict' |
|
|
|
/** |
|
* Module exports. |
|
* @public |
|
*/ |
|
|
|
module.exports = rangeParser |
|
|
|
/** |
|
* Parse "Range" header `str` relative to the given file `size`. |
|
* |
|
* @param {Number} size |
|
* @param {String} str |
|
* @param {Object} [options] |
|
* @return {Array} |
|
* @public |
|
*/ |
|
|
|
function rangeParser (size, str, options) { |
|
if (typeof str !== 'string') { |
|
throw new TypeError('argument str must be a string') |
|
} |
|
|
|
var index = str.indexOf('=') |
|
|
|
if (index === -1) { |
|
return -2 |
|
} |
|
|
|
// split the range string |
|
var arr = str.slice(index + 1).split(',') |
|
var ranges = [] |
|
|
|
// add ranges type |
|
ranges.type = str.slice(0, index) |
|
|
|
// parse all ranges |
|
for (var i = 0; i < arr.length; i++) { |
|
var range = arr[i].split('-') |
|
var start = parseInt(range[0], 10) |
|
var end = parseInt(range[1], 10) |
|
|
|
// -nnn |
|
if (isNaN(start)) { |
|
start = size - end |
|
end = size - 1 |
|
// nnn- |
|
} else if (isNaN(end)) { |
|
end = size - 1 |
|
} |
|
|
|
// limit last-byte-pos to current length |
|
if (end > size - 1) { |
|
end = size - 1 |
|
} |
|
|
|
// invalid or unsatisifiable |
|
if (isNaN(start) || isNaN(end) || start > end || start < 0) { |
|
continue |
|
} |
|
|
|
// add range |
|
ranges.push({ |
|
start: start, |
|
end: end |
|
}) |
|
} |
|
|
|
if (ranges.length < 1) { |
|
// unsatisifiable |
|
return -1 |
|
} |
|
|
|
return options && options.combine |
|
? combineRanges(ranges) |
|
: ranges |
|
} |
|
|
|
/** |
|
* Combine overlapping & adjacent ranges. |
|
* @private |
|
*/ |
|
|
|
function combineRanges (ranges) { |
|
var ordered = ranges.map(mapWithIndex).sort(sortByRangeStart) |
|
|
|
for (var j = 0, i = 1; i < ordered.length; i++) { |
|
var range = ordered[i] |
|
var current = ordered[j] |
|
|
|
if (range.start > current.end + 1) { |
|
// next range |
|
ordered[++j] = range |
|
} else if (range.end > current.end) { |
|
// extend range |
|
current.end = range.end |
|
current.index = Math.min(current.index, range.index) |
|
} |
|
} |
|
|
|
// trim ordered array |
|
ordered.length = j + 1 |
|
|
|
// generate combined range |
|
var combined = ordered.sort(sortByRangeIndex).map(mapWithoutIndex) |
|
|
|
// copy ranges type |
|
combined.type = ranges.type |
|
|
|
return combined |
|
} |
|
|
|
/** |
|
* Map function to add index value to ranges. |
|
* @private |
|
*/ |
|
|
|
function mapWithIndex (range, index) { |
|
return { |
|
start: range.start, |
|
end: range.end, |
|
index: index |
|
} |
|
} |
|
|
|
/** |
|
* Map function to remove index value from ranges. |
|
* @private |
|
*/ |
|
|
|
function mapWithoutIndex (range) { |
|
return { |
|
start: range.start, |
|
end: range.end |
|
} |
|
} |
|
|
|
/** |
|
* Sort function to sort ranges by index. |
|
* @private |
|
*/ |
|
|
|
function sortByRangeIndex (a, b) { |
|
return a.index - b.index |
|
} |
|
|
|
/** |
|
* Sort function to sort ranges by start position. |
|
* @private |
|
*/ |
|
|
|
function sortByRangeStart (a, b) { |
|
return a.start - b.start |
|
}
|
|
|