mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-30 18:12:25 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			176 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // Copyright Joyent, Inc. and other Node contributors.
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a
 | |
| // copy of this software and associated documentation files (the
 | |
| // "Software"), to deal in the Software without restriction, including
 | |
| // without limitation the rights to use, copy, modify, merge, publish,
 | |
| // distribute, sublicense, and/or sell copies of the Software, and to permit
 | |
| // persons to whom the Software is furnished to do so, subject to the
 | |
| // following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included
 | |
| // in all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | |
| // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 | |
| // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 | |
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 | |
| // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 | |
| // USE OR OTHER DEALINGS IN THE SOFTWARE.
 | |
| // a transform stream is a readable/writable stream where you do
 | |
| // something with the data.  Sometimes it's called a "filter",
 | |
| // but that's not a great name for it, since that implies a thing where
 | |
| // some bits pass through, and others are simply ignored.  (That would
 | |
| // be a valid example of a transform, of course.)
 | |
| //
 | |
| // While the output is causally related to the input, it's not a
 | |
| // necessarily symmetric or synchronous transformation.  For example,
 | |
| // a zlib stream might take multiple plain-text writes(), and then
 | |
| // emit a single compressed chunk some time in the future.
 | |
| //
 | |
| // Here's how this works:
 | |
| //
 | |
| // The Transform stream has all the aspects of the readable and writable
 | |
| // stream classes.  When you write(chunk), that calls _write(chunk,cb)
 | |
| // internally, and returns false if there's a lot of pending writes
 | |
| // buffered up.  When you call read(), that calls _read(n) until
 | |
| // there's enough pending readable data buffered up.
 | |
| //
 | |
| // In a transform stream, the written data is placed in a buffer.  When
 | |
| // _read(n) is called, it transforms the queued up data, calling the
 | |
| // buffered _write cb's as it consumes chunks.  If consuming a single
 | |
| // written chunk would result in multiple output chunks, then the first
 | |
| // outputted bit calls the readcb, and subsequent chunks just go into
 | |
| // the read buffer, and will cause it to emit 'readable' if necessary.
 | |
| //
 | |
| // This way, back-pressure is actually determined by the reading side,
 | |
| // since _read has to be called to start processing a new chunk.  However,
 | |
| // a pathological inflate type of transform can cause excessive buffering
 | |
| // here.  For example, imagine a stream where every byte of input is
 | |
| // interpreted as an integer from 0-255, and then results in that many
 | |
| // bytes of output.  Writing the 4 bytes {ff,ff,ff,ff} would result in
 | |
| // 1kb of data being output.  In this case, you could write a very small
 | |
| // amount of input, and end up with a very large amount of output.  In
 | |
| // such a pathological inflating mechanism, there'd be no way to tell
 | |
| // the system to stop doing the transform.  A single 4MB write could
 | |
| // cause the system to run out of memory.
 | |
| //
 | |
| // However, even in such a pathological case, only a single written chunk
 | |
| // would be consumed, and then the rest would wait (un-transformed) until
 | |
| // the results of the previous transformed chunk were consumed.
 | |
| 'use strict'
 | |
| 
 | |
| const { ObjectSetPrototypeOf, Symbol } = require('../../ours/primordials')
 | |
| 
 | |
| module.exports = Transform
 | |
| 
 | |
| const { ERR_METHOD_NOT_IMPLEMENTED } = require('../../ours/errors').codes
 | |
| 
 | |
| const Duplex = require('./duplex')
 | |
| 
 | |
| ObjectSetPrototypeOf(Transform.prototype, Duplex.prototype)
 | |
| ObjectSetPrototypeOf(Transform, Duplex)
 | |
| const kCallback = Symbol('kCallback')
 | |
| 
 | |
| function Transform(options) {
 | |
|   if (!(this instanceof Transform)) return new Transform(options)
 | |
|   Duplex.call(this, options) // We have implemented the _read method, and done the other things
 | |
|   // that Readable wants before the first _read call, so unset the
 | |
|   // sync guard flag.
 | |
| 
 | |
|   this._readableState.sync = false
 | |
|   this[kCallback] = null
 | |
| 
 | |
|   if (options) {
 | |
|     if (typeof options.transform === 'function') this._transform = options.transform
 | |
|     if (typeof options.flush === 'function') this._flush = options.flush
 | |
|   } // When the writable side finishes, then flush out anything remaining.
 | |
|   // Backwards compat. Some Transform streams incorrectly implement _final
 | |
|   // instead of or in addition to _flush. By using 'prefinish' instead of
 | |
|   // implementing _final we continue supporting this unfortunate use case.
 | |
| 
 | |
|   this.on('prefinish', prefinish)
 | |
| }
 | |
| 
 | |
| function final(cb) {
 | |
|   if (typeof this._flush === 'function' && !this.destroyed) {
 | |
|     this._flush((er, data) => {
 | |
|       if (er) {
 | |
|         if (cb) {
 | |
|           cb(er)
 | |
|         } else {
 | |
|           this.destroy(er)
 | |
|         }
 | |
| 
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       if (data != null) {
 | |
|         this.push(data)
 | |
|       }
 | |
| 
 | |
|       this.push(null)
 | |
| 
 | |
|       if (cb) {
 | |
|         cb()
 | |
|       }
 | |
|     })
 | |
|   } else {
 | |
|     this.push(null)
 | |
| 
 | |
|     if (cb) {
 | |
|       cb()
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| function prefinish() {
 | |
|   if (this._final !== final) {
 | |
|     final.call(this)
 | |
|   }
 | |
| }
 | |
| 
 | |
| Transform.prototype._final = final
 | |
| 
 | |
| Transform.prototype._transform = function (chunk, encoding, callback) {
 | |
|   throw new ERR_METHOD_NOT_IMPLEMENTED('_transform()')
 | |
| }
 | |
| 
 | |
| Transform.prototype._write = function (chunk, encoding, callback) {
 | |
|   const rState = this._readableState
 | |
|   const wState = this._writableState
 | |
|   const length = rState.length
 | |
| 
 | |
|   this._transform(chunk, encoding, (err, val) => {
 | |
|     if (err) {
 | |
|       callback(err)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     if (val != null) {
 | |
|       this.push(val)
 | |
|     }
 | |
| 
 | |
|     if (
 | |
|       wState.ended || // Backwards compat.
 | |
|       length === rState.length || // Backwards compat.
 | |
|       rState.length < rState.highWaterMark ||
 | |
|       rState.highWaterMark === 0 ||
 | |
|       rState.length === 0
 | |
|     ) {
 | |
|       callback()
 | |
|     } else {
 | |
|       this[kCallback] = callback
 | |
|     }
 | |
|   })
 | |
| }
 | |
| 
 | |
| Transform.prototype._read = function () {
 | |
|   if (this[kCallback]) {
 | |
|     const callback = this[kCallback]
 | |
|     this[kCallback] = null
 | |
|     callback()
 | |
|   }
 | |
| }
 |