PageRenderTime 58ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/node_modules/readable-stream/lib/_stream_writable.js

https://gitlab.com/jeaster12/gulp-nunjucks
JavaScript | 529 lines | 348 code | 102 blank | 79 comment | 68 complexity | 110d6ba9d172ab61612eedcdf741f83b MD5 | raw file
  1. // A bit simpler than readable streams.
  2. // Implement an async ._write(chunk, encoding, cb), and it'll handle all
  3. // the drain event emission and buffering.
  4. 'use strict';
  5. module.exports = Writable;
  6. /*<replacement>*/
  7. var processNextTick = require('process-nextick-args');
  8. /*</replacement>*/
  9. /*<replacement>*/
  10. var Buffer = require('buffer').Buffer;
  11. /*</replacement>*/
  12. Writable.WritableState = WritableState;
  13. /*<replacement>*/
  14. var util = require('core-util-is');
  15. util.inherits = require('inherits');
  16. /*</replacement>*/
  17. /*<replacement>*/
  18. var internalUtil = {
  19. deprecate: require('util-deprecate')
  20. };
  21. /*</replacement>*/
  22. /*<replacement>*/
  23. var Stream;
  24. (function (){try{
  25. Stream = require('st' + 'ream');
  26. }catch(_){}finally{
  27. if (!Stream)
  28. Stream = require('events').EventEmitter;
  29. }}())
  30. /*</replacement>*/
  31. var Buffer = require('buffer').Buffer;
  32. util.inherits(Writable, Stream);
  33. function nop() {}
  34. function WriteReq(chunk, encoding, cb) {
  35. this.chunk = chunk;
  36. this.encoding = encoding;
  37. this.callback = cb;
  38. this.next = null;
  39. }
  40. var Duplex;
  41. function WritableState(options, stream) {
  42. Duplex = Duplex || require('./_stream_duplex');
  43. options = options || {};
  44. // object stream flag to indicate whether or not this stream
  45. // contains buffers or objects.
  46. this.objectMode = !!options.objectMode;
  47. if (stream instanceof Duplex)
  48. this.objectMode = this.objectMode || !!options.writableObjectMode;
  49. // the point at which write() starts returning false
  50. // Note: 0 is a valid value, means that we always return false if
  51. // the entire buffer is not flushed immediately on write()
  52. var hwm = options.highWaterMark;
  53. var defaultHwm = this.objectMode ? 16 : 16 * 1024;
  54. this.highWaterMark = (hwm || hwm === 0) ? hwm : defaultHwm;
  55. // cast to ints.
  56. this.highWaterMark = ~~this.highWaterMark;
  57. this.needDrain = false;
  58. // at the start of calling end()
  59. this.ending = false;
  60. // when end() has been called, and returned
  61. this.ended = false;
  62. // when 'finish' is emitted
  63. this.finished = false;
  64. // should we decode strings into buffers before passing to _write?
  65. // this is here so that some node-core streams can optimize string
  66. // handling at a lower level.
  67. var noDecode = options.decodeStrings === false;
  68. this.decodeStrings = !noDecode;
  69. // Crypto is kind of old and crusty. Historically, its default string
  70. // encoding is 'binary' so we have to make this configurable.
  71. // Everything else in the universe uses 'utf8', though.
  72. this.defaultEncoding = options.defaultEncoding || 'utf8';
  73. // not an actual buffer we keep track of, but a measurement
  74. // of how much we're waiting to get pushed to some underlying
  75. // socket or file.
  76. this.length = 0;
  77. // a flag to see when we're in the middle of a write.
  78. this.writing = false;
  79. // when true all writes will be buffered until .uncork() call
  80. this.corked = 0;
  81. // a flag to be able to tell if the onwrite cb is called immediately,
  82. // or on a later tick. We set this to true at first, because any
  83. // actions that shouldn't happen until "later" should generally also
  84. // not happen before the first write call.
  85. this.sync = true;
  86. // a flag to know if we're processing previously buffered items, which
  87. // may call the _write() callback in the same tick, so that we don't
  88. // end up in an overlapped onwrite situation.
  89. this.bufferProcessing = false;
  90. // the callback that's passed to _write(chunk,cb)
  91. this.onwrite = function(er) {
  92. onwrite(stream, er);
  93. };
  94. // the callback that the user supplies to write(chunk,encoding,cb)
  95. this.writecb = null;
  96. // the amount that is being written when _write is called.
  97. this.writelen = 0;
  98. this.bufferedRequest = null;
  99. this.lastBufferedRequest = null;
  100. // number of pending user-supplied write callbacks
  101. // this must be 0 before 'finish' can be emitted
  102. this.pendingcb = 0;
  103. // emit prefinish if the only thing we're waiting for is _write cbs
  104. // This is relevant for synchronous Transform streams
  105. this.prefinished = false;
  106. // True if the error was already emitted and should not be thrown again
  107. this.errorEmitted = false;
  108. }
  109. WritableState.prototype.getBuffer = function writableStateGetBuffer() {
  110. var current = this.bufferedRequest;
  111. var out = [];
  112. while (current) {
  113. out.push(current);
  114. current = current.next;
  115. }
  116. return out;
  117. };
  118. (function (){try {
  119. Object.defineProperty(WritableState.prototype, 'buffer', {
  120. get: internalUtil.deprecate(function() {
  121. return this.getBuffer();
  122. }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' +
  123. 'instead.')
  124. });
  125. }catch(_){}}());
  126. var Duplex;
  127. function Writable(options) {
  128. Duplex = Duplex || require('./_stream_duplex');
  129. // Writable ctor is applied to Duplexes, though they're not
  130. // instanceof Writable, they're instanceof Readable.
  131. if (!(this instanceof Writable) && !(this instanceof Duplex))
  132. return new Writable(options);
  133. this._writableState = new WritableState(options, this);
  134. // legacy.
  135. this.writable = true;
  136. if (options) {
  137. if (typeof options.write === 'function')
  138. this._write = options.write;
  139. if (typeof options.writev === 'function')
  140. this._writev = options.writev;
  141. }
  142. Stream.call(this);
  143. }
  144. // Otherwise people can pipe Writable streams, which is just wrong.
  145. Writable.prototype.pipe = function() {
  146. this.emit('error', new Error('Cannot pipe. Not readable.'));
  147. };
  148. function writeAfterEnd(stream, cb) {
  149. var er = new Error('write after end');
  150. // TODO: defer error events consistently everywhere, not just the cb
  151. stream.emit('error', er);
  152. processNextTick(cb, er);
  153. }
  154. // If we get something that is not a buffer, string, null, or undefined,
  155. // and we're not in objectMode, then that's an error.
  156. // Otherwise stream chunks are all considered to be of length=1, and the
  157. // watermarks determine how many objects to keep in the buffer, rather than
  158. // how many bytes or characters.
  159. function validChunk(stream, state, chunk, cb) {
  160. var valid = true;
  161. if (!(Buffer.isBuffer(chunk)) &&
  162. typeof chunk !== 'string' &&
  163. chunk !== null &&
  164. chunk !== undefined &&
  165. !state.objectMode) {
  166. var er = new TypeError('Invalid non-string/buffer chunk');
  167. stream.emit('error', er);
  168. processNextTick(cb, er);
  169. valid = false;
  170. }
  171. return valid;
  172. }
  173. Writable.prototype.write = function(chunk, encoding, cb) {
  174. var state = this._writableState;
  175. var ret = false;
  176. if (typeof encoding === 'function') {
  177. cb = encoding;
  178. encoding = null;
  179. }
  180. if (Buffer.isBuffer(chunk))
  181. encoding = 'buffer';
  182. else if (!encoding)
  183. encoding = state.defaultEncoding;
  184. if (typeof cb !== 'function')
  185. cb = nop;
  186. if (state.ended)
  187. writeAfterEnd(this, cb);
  188. else if (validChunk(this, state, chunk, cb)) {
  189. state.pendingcb++;
  190. ret = writeOrBuffer(this, state, chunk, encoding, cb);
  191. }
  192. return ret;
  193. };
  194. Writable.prototype.cork = function() {
  195. var state = this._writableState;
  196. state.corked++;
  197. };
  198. Writable.prototype.uncork = function() {
  199. var state = this._writableState;
  200. if (state.corked) {
  201. state.corked--;
  202. if (!state.writing &&
  203. !state.corked &&
  204. !state.finished &&
  205. !state.bufferProcessing &&
  206. state.bufferedRequest)
  207. clearBuffer(this, state);
  208. }
  209. };
  210. Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
  211. // node::ParseEncoding() requires lower case.
  212. if (typeof encoding === 'string')
  213. encoding = encoding.toLowerCase();
  214. if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64',
  215. 'ucs2', 'ucs-2','utf16le', 'utf-16le', 'raw']
  216. .indexOf((encoding + '').toLowerCase()) > -1))
  217. throw new TypeError('Unknown encoding: ' + encoding);
  218. this._writableState.defaultEncoding = encoding;
  219. };
  220. function decodeChunk(state, chunk, encoding) {
  221. if (!state.objectMode &&
  222. state.decodeStrings !== false &&
  223. typeof chunk === 'string') {
  224. chunk = new Buffer(chunk, encoding);
  225. }
  226. return chunk;
  227. }
  228. // if we're already writing something, then just put this
  229. // in the queue, and wait our turn. Otherwise, call _write
  230. // If we return false, then we need a drain event, so set that flag.
  231. function writeOrBuffer(stream, state, chunk, encoding, cb) {
  232. chunk = decodeChunk(state, chunk, encoding);
  233. if (Buffer.isBuffer(chunk))
  234. encoding = 'buffer';
  235. var len = state.objectMode ? 1 : chunk.length;
  236. state.length += len;
  237. var ret = state.length < state.highWaterMark;
  238. // we must ensure that previous needDrain will not be reset to false.
  239. if (!ret)
  240. state.needDrain = true;
  241. if (state.writing || state.corked) {
  242. var last = state.lastBufferedRequest;
  243. state.lastBufferedRequest = new WriteReq(chunk, encoding, cb);
  244. if (last) {
  245. last.next = state.lastBufferedRequest;
  246. } else {
  247. state.bufferedRequest = state.lastBufferedRequest;
  248. }
  249. } else {
  250. doWrite(stream, state, false, len, chunk, encoding, cb);
  251. }
  252. return ret;
  253. }
  254. function doWrite(stream, state, writev, len, chunk, encoding, cb) {
  255. state.writelen = len;
  256. state.writecb = cb;
  257. state.writing = true;
  258. state.sync = true;
  259. if (writev)
  260. stream._writev(chunk, state.onwrite);
  261. else
  262. stream._write(chunk, encoding, state.onwrite);
  263. state.sync = false;
  264. }
  265. function onwriteError(stream, state, sync, er, cb) {
  266. --state.pendingcb;
  267. if (sync)
  268. processNextTick(cb, er);
  269. else
  270. cb(er);
  271. stream._writableState.errorEmitted = true;
  272. stream.emit('error', er);
  273. }
  274. function onwriteStateUpdate(state) {
  275. state.writing = false;
  276. state.writecb = null;
  277. state.length -= state.writelen;
  278. state.writelen = 0;
  279. }
  280. function onwrite(stream, er) {
  281. var state = stream._writableState;
  282. var sync = state.sync;
  283. var cb = state.writecb;
  284. onwriteStateUpdate(state);
  285. if (er)
  286. onwriteError(stream, state, sync, er, cb);
  287. else {
  288. // Check if we're actually ready to finish, but don't emit yet
  289. var finished = needFinish(state);
  290. if (!finished &&
  291. !state.corked &&
  292. !state.bufferProcessing &&
  293. state.bufferedRequest) {
  294. clearBuffer(stream, state);
  295. }
  296. if (sync) {
  297. processNextTick(afterWrite, stream, state, finished, cb);
  298. } else {
  299. afterWrite(stream, state, finished, cb);
  300. }
  301. }
  302. }
  303. function afterWrite(stream, state, finished, cb) {
  304. if (!finished)
  305. onwriteDrain(stream, state);
  306. state.pendingcb--;
  307. cb();
  308. finishMaybe(stream, state);
  309. }
  310. // Must force callback to be called on nextTick, so that we don't
  311. // emit 'drain' before the write() consumer gets the 'false' return
  312. // value, and has a chance to attach a 'drain' listener.
  313. function onwriteDrain(stream, state) {
  314. if (state.length === 0 && state.needDrain) {
  315. state.needDrain = false;
  316. stream.emit('drain');
  317. }
  318. }
  319. // if there's something in the buffer waiting, then process it
  320. function clearBuffer(stream, state) {
  321. state.bufferProcessing = true;
  322. var entry = state.bufferedRequest;
  323. if (stream._writev && entry && entry.next) {
  324. // Fast case, write everything using _writev()
  325. var buffer = [];
  326. var cbs = [];
  327. while (entry) {
  328. cbs.push(entry.callback);
  329. buffer.push(entry);
  330. entry = entry.next;
  331. }
  332. // count the one we are adding, as well.
  333. // TODO(isaacs) clean this up
  334. state.pendingcb++;
  335. state.lastBufferedRequest = null;
  336. doWrite(stream, state, true, state.length, buffer, '', function(err) {
  337. for (var i = 0; i < cbs.length; i++) {
  338. state.pendingcb--;
  339. cbs[i](err);
  340. }
  341. });
  342. // Clear buffer
  343. } else {
  344. // Slow case, write chunks one-by-one
  345. while (entry) {
  346. var chunk = entry.chunk;
  347. var encoding = entry.encoding;
  348. var cb = entry.callback;
  349. var len = state.objectMode ? 1 : chunk.length;
  350. doWrite(stream, state, false, len, chunk, encoding, cb);
  351. entry = entry.next;
  352. // if we didn't call the onwrite immediately, then
  353. // it means that we need to wait until it does.
  354. // also, that means that the chunk and cb are currently
  355. // being processed, so move the buffer counter past them.
  356. if (state.writing) {
  357. break;
  358. }
  359. }
  360. if (entry === null)
  361. state.lastBufferedRequest = null;
  362. }
  363. state.bufferedRequest = entry;
  364. state.bufferProcessing = false;
  365. }
  366. Writable.prototype._write = function(chunk, encoding, cb) {
  367. cb(new Error('not implemented'));
  368. };
  369. Writable.prototype._writev = null;
  370. Writable.prototype.end = function(chunk, encoding, cb) {
  371. var state = this._writableState;
  372. if (typeof chunk === 'function') {
  373. cb = chunk;
  374. chunk = null;
  375. encoding = null;
  376. } else if (typeof encoding === 'function') {
  377. cb = encoding;
  378. encoding = null;
  379. }
  380. if (chunk !== null && chunk !== undefined)
  381. this.write(chunk, encoding);
  382. // .end() fully uncorks
  383. if (state.corked) {
  384. state.corked = 1;
  385. this.uncork();
  386. }
  387. // ignore unnecessary end() calls.
  388. if (!state.ending && !state.finished)
  389. endWritable(this, state, cb);
  390. };
  391. function needFinish(state) {
  392. return (state.ending &&
  393. state.length === 0 &&
  394. state.bufferedRequest === null &&
  395. !state.finished &&
  396. !state.writing);
  397. }
  398. function prefinish(stream, state) {
  399. if (!state.prefinished) {
  400. state.prefinished = true;
  401. stream.emit('prefinish');
  402. }
  403. }
  404. function finishMaybe(stream, state) {
  405. var need = needFinish(state);
  406. if (need) {
  407. if (state.pendingcb === 0) {
  408. prefinish(stream, state);
  409. state.finished = true;
  410. stream.emit('finish');
  411. } else {
  412. prefinish(stream, state);
  413. }
  414. }
  415. return need;
  416. }
  417. function endWritable(stream, state, cb) {
  418. state.ending = true;
  419. finishMaybe(stream, state);
  420. if (cb) {
  421. if (state.finished)
  422. processNextTick(cb);
  423. else
  424. stream.once('finish', cb);
  425. }
  426. state.ended = true;
  427. }