index.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. var index = str.indexOf('=')
  24. if (index === -1) {
  25. return -2
  26. }
  27. // split the range string
  28. var arr = str.slice(index + 1).split(',')
  29. var ranges = []
  30. // add ranges type
  31. ranges.type = str.slice(0, index)
  32. // parse all ranges
  33. for (var i = 0; i < arr.length; i++) {
  34. var range = arr[i].split('-')
  35. var start = parseInt(range[0], 10)
  36. var end = parseInt(range[1], 10)
  37. // -nnn
  38. if (isNaN(start)) {
  39. start = size - end
  40. end = size - 1
  41. // nnn-
  42. } else if (isNaN(end)) {
  43. end = size - 1
  44. }
  45. // limit last-byte-pos to current length
  46. if (end > size - 1) {
  47. end = size - 1
  48. }
  49. // invalid or unsatisifiable
  50. if (isNaN(start) || isNaN(end) || start > end || start < 0) {
  51. continue
  52. }
  53. // add range
  54. ranges.push({
  55. start: start,
  56. end: end
  57. })
  58. }
  59. if (ranges.length < 1) {
  60. // unsatisifiable
  61. return -1
  62. }
  63. return options && options.combine
  64. ? combineRanges(ranges)
  65. : ranges
  66. }
  67. /**
  68. * Combine overlapping & adjacent ranges.
  69. * @private
  70. */
  71. function combineRanges (ranges) {
  72. var ordered = ranges.map(mapWithIndex).sort(sortByRangeStart)
  73. for (var j = 0, i = 1; i < ordered.length; i++) {
  74. var range = ordered[i]
  75. var current = ordered[j]
  76. if (range.start > current.end + 1) {
  77. // next range
  78. ordered[++j] = range
  79. } else if (range.end > current.end) {
  80. // extend range
  81. current.end = range.end
  82. current.index = Math.min(current.index, range.index)
  83. }
  84. }
  85. // trim ordered array
  86. ordered.length = j + 1
  87. // generate combined range
  88. var combined = ordered.sort(sortByRangeIndex).map(mapWithoutIndex)
  89. // copy ranges type
  90. combined.type = ranges.type
  91. return combined
  92. }
  93. /**
  94. * Map function to add index value to ranges.
  95. * @private
  96. */
  97. function mapWithIndex (range, index) {
  98. return {
  99. start: range.start,
  100. end: range.end,
  101. index: index
  102. }
  103. }
  104. /**
  105. * Map function to remove index value from ranges.
  106. * @private
  107. */
  108. function mapWithoutIndex (range) {
  109. return {
  110. start: range.start,
  111. end: range.end
  112. }
  113. }
  114. /**
  115. * Sort function to sort ranges by index.
  116. * @private
  117. */
  118. function sortByRangeIndex (a, b) {
  119. return a.index - b.index
  120. }
  121. /**
  122. * Sort function to sort ranges by start position.
  123. * @private
  124. */
  125. function sortByRangeStart (a, b) {
  126. return a.start - b.start
  127. }