NGL@1.0.0-beta.7 Home Manual Reference Source Gallery

src/streamer/streamer.js

  1. /**
  2. * @file Streamer
  3. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  4. * @private
  5. */
  6.  
  7. import { DecompressorRegistry } from '../globals.js'
  8. import { uint8ToString, defaults } from '../utils.js'
  9.  
  10. class Streamer {
  11. constructor (src, params) {
  12. const p = params || {}
  13.  
  14. this.compressed = defaults(p.compressed, false)
  15. this.binary = defaults(p.binary, false)
  16. this.json = defaults(p.json, false)
  17. this.xml = defaults(p.xml, false)
  18.  
  19. this.src = src
  20. this.chunkSize = 1024 * 1024 * 10
  21. this.newline = '\n'
  22.  
  23. this.__pointer = 0
  24. this.__partialLine = ''
  25.  
  26. if (this.__srcName) {
  27. this[ this.__srcName ] = src
  28. }
  29. }
  30.  
  31. get type () { return '' }
  32.  
  33. get __srcName () { return undefined }
  34.  
  35. isBinary () {
  36. return this.binary || this.compressed
  37. }
  38.  
  39. onload () {}
  40.  
  41. onprogress () {}
  42.  
  43. onerror () {}
  44.  
  45. read () {
  46. return new Promise((resolve, reject) => {
  47. try {
  48. this._read(data => {
  49. const decompressFn = DecompressorRegistry.get(this.compressed)
  50.  
  51. if (this.compressed && decompressFn) {
  52. this.data = decompressFn(data)
  53. } else {
  54. if ((this.binary || this.compressed) && data instanceof ArrayBuffer) {
  55. data = new Uint8Array(data)
  56. }
  57. this.data = data
  58. }
  59.  
  60. resolve(this.data)
  61. })
  62. } catch (e) {
  63. reject(e)
  64. }
  65. })
  66. }
  67.  
  68. _read (callback) {
  69. // overwrite this method when this.src does not contain the data
  70. callback(this.src)
  71. }
  72.  
  73. _chunk (start, end) {
  74. end = Math.min(this.data.length, end)
  75.  
  76. if (start === 0 && this.data.length === end) {
  77. return this.data
  78. } else {
  79. if (this.isBinary()) {
  80. return this.data.subarray(start, end)
  81. } else {
  82. return this.data.substring(start, end)
  83. }
  84. }
  85. }
  86.  
  87. chunk (start) {
  88. const end = start + this.chunkSize
  89.  
  90. return this._chunk(start, end)
  91. }
  92.  
  93. peekLines (m) {
  94. const data = this.data
  95. const n = data.length
  96.  
  97. // FIXME does not work for multi-char newline
  98. const newline = this.isBinary() ? this.newline.charCodeAt(0) : this.newline
  99.  
  100. let i
  101. let count = 0
  102. for (i = 0; i < n; ++i) {
  103. if (data[ i ] === newline) ++count
  104. if (count === m) break
  105. }
  106.  
  107. const chunk = this._chunk(0, i + 1)
  108. const d = this.chunkToLines(chunk, '', i > n)
  109.  
  110. return d.lines
  111. }
  112.  
  113. chunkCount () {
  114. return Math.floor(this.data.length / this.chunkSize) + 1
  115. }
  116.  
  117. asText () {
  118. return this.isBinary() ? uint8ToString(this.data) : this.data
  119. }
  120.  
  121. chunkToLines (chunk, partialLine, isLast) {
  122. const newline = this.newline
  123.  
  124. if (!this.isBinary() && chunk.length === this.data.length) {
  125. return {
  126. lines: chunk.split(newline),
  127. partialLine: ''
  128. }
  129. }
  130.  
  131. let lines = []
  132. const str = this.isBinary() ? uint8ToString(chunk) : chunk
  133. const idx = str.lastIndexOf(newline)
  134.  
  135. if (idx === -1) {
  136. partialLine += str
  137. } else {
  138. const str2 = partialLine + str.substr(0, idx)
  139. lines = lines.concat(str2.split(newline))
  140.  
  141. if (idx === str.length - newline.length) {
  142. partialLine = ''
  143. } else {
  144. partialLine = str.substr(idx + newline.length)
  145. }
  146. }
  147.  
  148. if (isLast && partialLine !== '') {
  149. lines.push(partialLine)
  150. }
  151.  
  152. return {
  153. lines: lines,
  154. partialLine: partialLine
  155. }
  156. }
  157.  
  158. nextChunk () {
  159. const start = this.__pointer
  160.  
  161. if (start > this.data.length) {
  162. return undefined
  163. }
  164.  
  165. this.__pointer += this.chunkSize
  166. return this.chunk(start)
  167. }
  168.  
  169. nextChunkOfLines () {
  170. const chunk = this.nextChunk()
  171.  
  172. if (chunk === undefined) {
  173. return undefined
  174. }
  175.  
  176. const isLast = this.__pointer > this.data.length
  177. const d = this.chunkToLines(chunk, this.__partialLine, isLast)
  178.  
  179. this.__partialLine = d.partialLine
  180.  
  181. return d.lines
  182. }
  183.  
  184. eachChunk (callback) {
  185. const chunkSize = this.chunkSize
  186. const n = this.data.length
  187. const chunkCount = this.chunkCount()
  188.  
  189. for (let i = 0; i < n; i += chunkSize) {
  190. const chunk = this.chunk(i)
  191. const chunkNo = Math.round(i / chunkSize)
  192.  
  193. callback(chunk, chunkNo, chunkCount)
  194. }
  195. }
  196.  
  197. eachChunkOfLines (callback) {
  198. this.eachChunk((chunk, chunkNo, chunkCount) => {
  199. const isLast = chunkNo === chunkCount + 1
  200. const d = this.chunkToLines(chunk, this.__partialLine, isLast)
  201.  
  202. this.__partialLine = d.partialLine
  203.  
  204. callback(d.lines, chunkNo, chunkCount)
  205. })
  206. }
  207.  
  208. dispose () {
  209. delete this.src
  210.  
  211. if (this.__srcName) {
  212. delete this[ this.__srcName ]
  213. }
  214. }
  215. }
  216.  
  217. export default Streamer