index.js 2.8 KB

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