michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "stability": "experimental" michael@0: }; michael@0: michael@0: exports.ByteReader = ByteReader; michael@0: exports.ByteWriter = ByteWriter; michael@0: michael@0: const {Cc, Ci} = require("chrome"); michael@0: michael@0: // This just controls the maximum number of bytes we read in at one time. michael@0: const BUFFER_BYTE_LEN = 0x8000; michael@0: michael@0: function ByteReader(inputStream) { michael@0: const self = this; michael@0: michael@0: let stream = Cc["@mozilla.org/binaryinputstream;1"]. michael@0: createInstance(Ci.nsIBinaryInputStream); michael@0: stream.setInputStream(inputStream); michael@0: michael@0: let manager = new StreamManager(this, stream); michael@0: michael@0: this.read = function ByteReader_read(numBytes) { michael@0: manager.ensureOpened(); michael@0: if (typeof(numBytes) !== "number") michael@0: numBytes = Infinity; michael@0: michael@0: let data = ""; michael@0: let read = 0; michael@0: try { michael@0: while (true) { michael@0: let avail = stream.available(); michael@0: let toRead = Math.min(numBytes - read, avail, BUFFER_BYTE_LEN); michael@0: if (toRead <= 0) michael@0: break; michael@0: data += stream.readBytes(toRead); michael@0: read += toRead; michael@0: } michael@0: } michael@0: catch (err) { michael@0: throw new Error("Error reading from stream: " + err); michael@0: } michael@0: michael@0: return data; michael@0: }; michael@0: } michael@0: michael@0: function ByteWriter(outputStream) { michael@0: const self = this; michael@0: michael@0: let stream = Cc["@mozilla.org/binaryoutputstream;1"]. michael@0: createInstance(Ci.nsIBinaryOutputStream); michael@0: stream.setOutputStream(outputStream); michael@0: michael@0: let manager = new StreamManager(this, stream); michael@0: michael@0: this.write = function ByteWriter_write(str) { michael@0: manager.ensureOpened(); michael@0: try { michael@0: stream.writeBytes(str, str.length); michael@0: } michael@0: catch (err) { michael@0: throw new Error("Error writing to stream: " + err); michael@0: } michael@0: }; michael@0: } michael@0: michael@0: michael@0: // This manages the lifetime of stream, a ByteReader or ByteWriter. It defines michael@0: // closed and close() on stream and registers an unload listener that closes michael@0: // rawStream if it's still opened. It also provides ensureOpened(), which michael@0: // throws an exception if the stream is closed. michael@0: function StreamManager(stream, rawStream) { michael@0: const self = this; michael@0: this.rawStream = rawStream; michael@0: this.opened = true; michael@0: michael@0: stream.__defineGetter__("closed", function stream_closed() { michael@0: return !self.opened; michael@0: }); michael@0: michael@0: stream.close = function stream_close() { michael@0: self.ensureOpened(); michael@0: self.unload(); michael@0: }; michael@0: michael@0: require("../system/unload").ensure(this); michael@0: } michael@0: michael@0: StreamManager.prototype = { michael@0: ensureOpened: function StreamManager_ensureOpened() { michael@0: if (!this.opened) michael@0: throw new Error("The stream is closed and cannot be used."); michael@0: }, michael@0: unload: function StreamManager_unload() { michael@0: this.rawStream.close(); michael@0: this.opened = false; michael@0: } michael@0: };