utils.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. 'use strict'
  2. var util = require('util')
  3. var isNode = require('detect-node')
  4. // Node.js 0.8, 0.10 and 0.12 support
  5. Object.assign = (process.versions.modules >= 46 || !isNode)
  6. ? Object.assign // eslint-disable-next-line
  7. : util._extend
  8. function QueueItem () {
  9. this.prev = null
  10. this.next = null
  11. }
  12. exports.QueueItem = QueueItem
  13. function Queue () {
  14. QueueItem.call(this)
  15. this.prev = this
  16. this.next = this
  17. }
  18. util.inherits(Queue, QueueItem)
  19. exports.Queue = Queue
  20. Queue.prototype.insertTail = function insertTail (item) {
  21. item.prev = this.prev
  22. item.next = this
  23. item.prev.next = item
  24. item.next.prev = item
  25. }
  26. Queue.prototype.remove = function remove (item) {
  27. var next = item.next
  28. var prev = item.prev
  29. item.next = item
  30. item.prev = item
  31. next.prev = prev
  32. prev.next = next
  33. }
  34. Queue.prototype.head = function head () {
  35. return this.next
  36. }
  37. Queue.prototype.tail = function tail () {
  38. return this.prev
  39. }
  40. Queue.prototype.isEmpty = function isEmpty () {
  41. return this.next === this
  42. }
  43. Queue.prototype.isRoot = function isRoot (item) {
  44. return this === item
  45. }
  46. function LockStream (stream) {
  47. this.locked = false
  48. this.queue = []
  49. this.stream = stream
  50. }
  51. exports.LockStream = LockStream
  52. LockStream.prototype.write = function write (chunks, callback) {
  53. var self = this
  54. // Do not let it interleave
  55. if (this.locked) {
  56. this.queue.push(function () {
  57. return self.write(chunks, callback)
  58. })
  59. return
  60. }
  61. this.locked = true
  62. function done (err, chunks) {
  63. self.stream.removeListener('error', done)
  64. self.locked = false
  65. if (self.queue.length > 0) { self.queue.shift()() }
  66. callback(err, chunks)
  67. }
  68. this.stream.on('error', done)
  69. // Accumulate all output data
  70. var output = []
  71. function onData (chunk) {
  72. output.push(chunk)
  73. }
  74. this.stream.on('data', onData)
  75. function next (err) {
  76. self.stream.removeListener('data', onData)
  77. if (err) {
  78. return done(err)
  79. }
  80. done(null, output)
  81. }
  82. for (var i = 0; i < chunks.length - 1; i++) { this.stream.write(chunks[i]) }
  83. if (chunks.length > 0) {
  84. this.stream.write(chunks[i], next)
  85. } else { process.nextTick(next) }
  86. if (this.stream.execute) {
  87. this.stream.execute(function (err) {
  88. if (err) { return done(err) }
  89. })
  90. }
  91. }
  92. // Just finds the place in array to insert
  93. function binaryLookup (list, item, compare) {
  94. var start = 0
  95. var end = list.length
  96. while (start < end) {
  97. var pos = (start + end) >> 1
  98. var cmp = compare(item, list[pos])
  99. if (cmp === 0) {
  100. start = pos
  101. end = pos
  102. break
  103. } else if (cmp < 0) {
  104. end = pos
  105. } else {
  106. start = pos + 1
  107. }
  108. }
  109. return start
  110. }
  111. exports.binaryLookup = binaryLookup
  112. function binaryInsert (list, item, compare) {
  113. var index = binaryLookup(list, item, compare)
  114. list.splice(index, 0, item)
  115. }
  116. exports.binaryInsert = binaryInsert
  117. function binarySearch (list, item, compare) {
  118. var index = binaryLookup(list, item, compare)
  119. if (index >= list.length) {
  120. return -1
  121. }
  122. if (compare(item, list[index]) === 0) {
  123. return index
  124. }
  125. return -1
  126. }
  127. exports.binarySearch = binarySearch
  128. function Timeout (object) {
  129. this.delay = 0
  130. this.timer = null
  131. this.object = object
  132. }
  133. exports.Timeout = Timeout
  134. Timeout.prototype.set = function set (delay, callback) {
  135. this.delay = delay
  136. this.reset()
  137. if (!callback) { return }
  138. if (this.delay === 0) {
  139. this.object.removeListener('timeout', callback)
  140. } else {
  141. this.object.once('timeout', callback)
  142. }
  143. }
  144. Timeout.prototype.reset = function reset () {
  145. if (this.timer !== null) {
  146. clearTimeout(this.timer)
  147. this.timer = null
  148. }
  149. if (this.delay === 0) { return }
  150. var self = this
  151. this.timer = setTimeout(function () {
  152. self.timer = null
  153. self.object.emit('timeout')
  154. }, this.delay)
  155. }