Bug 771617 - OS.File.prototype.{readTo, writeFrom, readAll}. r=froydnj
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Sat, 25 Aug 2012 17:18:43 -0400
changeset 105487 e555d4f92c1dd06e24abf8734141dfc02634b636
parent 105486 719af14ab13364758f9126cace04883e9733a5fb
child 105488 7b6763a02065f5901e5f6db6a419d714b0282567
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersfroydnj
bugs771617
milestone17.0a1
Bug 771617 - OS.File.prototype.{readTo, writeFrom, readAll}. r=froydnj
toolkit/components/osfile/osfile_shared_front.jsm
--- a/toolkit/components/osfile/osfile_shared_front.jsm
+++ b/toolkit/components/osfile/osfile_shared_front.jsm
@@ -9,16 +9,20 @@
  * be executed only on a worker thread.
  */
 
 if (typeof Components != "undefined") {
   throw new Error("osfile_shared_front.jsm cannot be used from the main thread");
 }
 (function(exports) {
 
+let LOG = exports.OS.Shared.LOG.bind(OS.Shared, "Shared front-end");
+
+const noOptions = {};
+
 /**
  * Code shared by implementations of File.
  *
  * @param {*} fd An OS-specific file handle.
  * @constructor
  */
 let AbstractFile = function AbstractFile(fd) {
   this._fd = fd;
@@ -30,19 +34,156 @@ AbstractFile.prototype = {
    *
    * @throw OS.File.Error if the file has been closed.
    */
   get fd() {
     if (this._fd) {
       return this._fd;
     }
     throw OS.File.Error.closed();
+  },
+  /**
+   * Read bytes from this file to a new buffer.
+   *
+   * @param {number=} bytes If unspecified, read all the remaining bytes from
+   * this file. If specified, read |bytes| bytes, or less if the file does not
+   * contain that many bytes.
+   * @return {buffer: ArrayBuffer, bytes: bytes} A buffer containing the
+   * bytes read and the number of bytes read. Note that |buffer| may be
+   * larger than the number of bytes actually read.
+   */
+  readAll: function readAll(bytes) {
+    if (bytes == null) {
+      bytes = this.stat().size;
+    }
+    let buffer = new ArrayBuffer(bytes);
+    let size = this.readTo(buffer, bytes);
+    return {
+      buffer: buffer,
+      bytes: size
+    };
+  },
+
+  /**
+   * Read bytes from this file to an existing buffer.
+   *
+   * Note that, by default, this function may perform several I/O
+   * operations to ensure that the buffer is as full as possible.
+   *
+   * @param {ArrayBuffer | C pointer} buffer The buffer in which to
+   * store the bytes. If options.offset is not given, bytes are stored
+   * from the start of the array. The buffer must be large enough to
+   * accomodate |bytes| bytes.
+   * @param {*=} options Optionally, an object that may contain the
+   * following fields:
+   * - {number} offset The offset in |buffer| at which to start placing
+   * data
+   *
+   * @return {number} The number of bytes actually read, which may be
+   * less than |bytes| if the file did not contain that many bytes left
+   * or if |options.once| was set.
+   */
+  readTo: function readTo(buffer, bytes, options) {
+    options = options || noOptions;
+
+    let pointer = AbstractFile.normalizeToPointer(buffer, bytes,
+      options.offset);
+    let pos = 0;
+    while (pos < bytes) {
+      let chunkSize = this.read(pointer, bytes - pos, options);
+      if (chunkSize == 0) {
+        break;
+      }
+      pos += chunkSize;
+      pointer = exports.OS.Shared.offsetBy(pointer, chunkSize);
+    }
+
+    return pos;
+  },
+
+  /**
+   * Write bytes from a buffer to this file.
+   *
+   * Note that, by default, this function may perform several I/O
+   * operations to ensure that the buffer is fully written.
+   *
+   * @param {ArrayBuffer | C pointer} buffer The buffer in which the
+   * the bytes are stored. If options.offset is not given, bytes are stored
+   * from the start of the array. The buffer must be large enough to
+   * accomodate |bytes| bytes.
+   * @param {*=} options Optionally, an object that may contain the
+   * following fields:
+   * - {number} offset The offset in |buffer| at which to start extracting
+   * data
+   *
+   * @return {number} The number of bytes actually written, which may be
+   * less than |bytes| if |options.once| was set.
+   */
+  writeFrom: function writeFrom(buffer, bytes, options) {
+    options = options || noOptions;
+
+    let pointer = AbstractFile.normalizeToPointer(buffer, bytes,
+      options.offset);
+
+    let pos = 0;
+    while (pos < bytes) {
+      let chunkSize = this.write(pointer, bytes - pos, options);
+      pos += chunkSize;
+      pointer = exports.OS.Shared.offsetBy(pointer, chunkSize);
+    }
+    return pos;
   }
 };
 
+/**
+ * Utility function used to normalize a ArrayBuffer or C pointer into a uint8_t
+ * C pointer.
+ *
+ * Future versions might extend this to other data structures.
+ *
+ * @param {ArrayBuffer|C pointer} candidate Either an ArrayBuffer or a
+ * non-null C pointer.
+ * @param {number} bytes The number of bytes that |candidate| should contain.
+ * Used for sanity checking if the size of |candidate| can be determined.
+ * @param {number=} offset Optionally, a number of bytes by which to shift
+ * |candidate|.
+ *
+ * @return {C pointer} A C pointer of type uint8_t, corresponding to the
+ * start of |candidate| + |offset| bytes.
+ */
+AbstractFile.normalizeToPointer = function normalizeToPointer(candidate, bytes, offset) {
+  if (!candidate) {
+    throw new TypeError("Expecting a C pointer or an ArrayBuffer");
+  }
+  if (offset == null) {
+    offset = 0;
+  }
+  let ptr;
+  if ("isNull" in candidate) {
+    if (candidate.isNull()) {
+      throw new TypeError("Expecting a non-null pointer");
+    }
+    ptr = exports.OS.Shared.Type.uint8_t.out_ptr.cast(candidate);
+  } else if ("byteLength" in candidate) {
+    ptr = exports.OS.Shared.Type.uint8_t.out_ptr.implementation(candidate);
+    if (candidate.byteLength < offset + bytes) {
+      throw new TypeError("Buffer is too short. I need at least " +
+                         (offset + bytes) +
+                         " bytes but I have only " +
+                         buffer.byteLength +
+                          "bytes");
+    }
+  } else {
+    throw new TypeError("Expecting a C pointer or an ArrayBuffer");
+  }
+  if (offset != 0) {
+    ptr = exports.OS.Shared.offsetBy(ptr, offset);
+  }
+  return ptr;
+};
 
 /**
  * Utility function shared by implementations of |OS.File.open|:
  * extract read/write/trunc/create/existing flags from a |mode|
  * object.
  *
  * @param {*=} mode An object that may contain fields |read|,
  * |write|, |truncate|, |create|, |existing|. These fields