Bug 1077354 - Transfer buffers to OS.File methods rather than passing a pointer r=froydnj
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 08 Jan 2015 11:31:21 +0000
changeset 222745 b777166f71b006e0de2b016bc0d06e8efd1d103b
parent 222744 e6ac6c211626ef78876625431e214df72f1f22ca
child 222746 68d2b24c9351576aede149f46d52cb0e97624ff7
push id28073
push userkwierso@gmail.com
push dateFri, 09 Jan 2015 01:08:23 +0000
treeherdermozilla-central@b3f84cf78dc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1077354
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1077354 - Transfer buffers to OS.File methods rather than passing a pointer r=froydnj
toolkit/components/osfile/modules/osfile_async_front.jsm
toolkit/components/osfile/modules/osfile_shared_allthreads.jsm
toolkit/components/osfile/modules/osfile_shared_front.jsm
toolkit/components/osfile/modules/osfile_unix_front.jsm
toolkit/components/osfile/modules/osfile_win_front.jsm
toolkit/components/osfile/tests/mochi/worker_test_osfile_comms.js
toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
toolkit/components/osfile/tests/xpcshell/test_compression.js
toolkit/components/osfile/tests/xpcshell/test_read_write.js
toolkit/components/workerlz4/lz4.js
toolkit/modules/ZipUtils.jsm
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -642,20 +642,16 @@ File.prototype = {
     // by communication.
     // Options might be a nullish value, so better check for that before using
     // the |in| operator.
     if (isTypedArray(buffer) && !(options && "bytes" in options)) {
       // Preserve reference to option |outExecutionDuration|, if it is passed.
       options = clone(options, ["outExecutionDuration"]);
       options.bytes = buffer.byteLength;
     }
-    // Note: Type.void_t.out_ptr.toMsg ensures that
-    // - the buffer is effectively shared (not neutered) between both
-    //   threads;
-    // - we take care of any |byteOffset|.
     return Scheduler.post("File_prototype_write",
       [this._fdmsg,
        Type.void_t.in_ptr.toMsg(buffer),
        options],
        buffer/*Ensure that |buffer| is not gc-ed*/);
   },
 
   /**
@@ -1157,20 +1153,16 @@ File.writeAtomic = function writeAtomic(
   options = clone(options, ["outExecutionDuration"]);
   // As options.tmpPath is a path, we need to encode it as |Type.path| message
   if ("tmpPath" in options) {
     options.tmpPath = Type.path.toMsg(options.tmpPath);
   };
   if (isTypedArray(buffer) && (!("bytes" in options))) {
     options.bytes = buffer.byteLength;
   };
-  // Note: Type.void_t.out_ptr.toMsg ensures that
-  // - the buffer is effectively shared (not neutered) between both
-  //   threads;
-  // - we take care of any |byteOffset|.
   let refObj = {};
   TelemetryStopwatch.start("OSFILE_WRITEATOMIC_JANK_MS", refObj);
   let promise = Scheduler.post("writeAtomic",
     [Type.path.toMsg(path),
      Type.void_t.in_ptr.toMsg(buffer),
      options], [options, buffer]);
   TelemetryStopwatch.finish("OSFILE_WRITEATOMIC_JANK_MS", refObj);
   return promise;
--- a/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm
+++ b/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm
@@ -20,42 +20,55 @@
 // Boilerplate used to be able to import this module both from the main
 // thread and from worker threads.
 
 // Since const is lexically scoped, hoist the
 // conditionally-useful definition ourselves.
 const Cu = typeof Components != "undefined" ? Components.utils : undefined;
 const Ci = typeof Components != "undefined" ? Components.interfaces : undefined;
 const Cc = typeof Components != "undefined" ? Components.classes : undefined;
+
+/**
+ * A constructor for messages that require transfers instead of copies.
+ *
+ * See BasePromiseWorker.Meta.
+ *
+ * @constructor
+ */
+let Meta;
 if (typeof Components != "undefined") {
   // Global definition of |exports|, to keep everybody happy.
   // In non-main thread, |exports| is provided by the module
   // loader.
   this.exports = {};
 
   Cu.import("resource://gre/modules/Services.jsm", this);
+  Meta = Cu.import("resource://gre/modules/PromiseWorker.jsm", {}).BasePromiseWorker.Meta;
+} else {
+  importScripts("resource://gre/modules/workers/require.js");
+  Meta = require("resource://gre/modules/workers/PromiseWorker.js").Meta;
 }
 
 let EXPORTED_SYMBOLS = [
   "LOG",
   "clone",
   "Config",
   "Constants",
   "Type",
   "HollowStructure",
   "OSError",
   "Library",
   "declareFFI",
   "declareLazy",
   "declareLazyFFI",
-  "normalizeToPointer",
+  "normalizeBufferArgs",
   "projectValue",
+  "isArrayBuffer",
   "isTypedArray",
   "defineLazyGetter",
-  "offsetBy",
   "OS" // Warning: this exported symbol will disappear
 ];
 
 ////////////////////// Configuration of OS.File
 
 let Config = {
   /**
    * If |true|, calls to |LOG| are shown. Otherwise, they are hidden.
@@ -136,17 +149,25 @@ let stringifyArg = function stringifyArg
     /**
      * The only way to detect whether this object has a non-default
      * implementation of |toString| is to check whether it returns
      * '[object Object]'. Unfortunately, we cannot simply compare |arg.toString|
      * and |Object.prototype.toString| as |arg| typically comes from another
      * compartment.
      */
     if (argToString === "[object Object]") {
-      return JSON.stringify(arg);
+      return JSON.stringify(arg, function(key, value) {
+        if (isTypedArray(value)) {
+          return "["+ value.constructor.name + " " + value.byteOffset + " " + value.byteLength + "]";
+        }
+        if (isArrayBuffer(arg)) {
+          return "[" + value.constructor.name + " " + value.byteLength + "]";
+        }
+        return value;
+      });
     } else {
       return argToString;
     }
   }
   return arg;
 };
 
 let LOG = function (...args) {
@@ -382,22 +403,31 @@ Type.prototype = {
     return this.implementation.size;
   }
 };
 
 /**
  * Utility function used to determine whether an object is a typed array
  */
 let isTypedArray = function isTypedArray(obj) {
-  return typeof obj == "object"
+  return obj != null && typeof obj == "object"
     && "byteOffset" in obj;
 };
 exports.isTypedArray = isTypedArray;
 
 /**
+ * Utility function used to determine whether an object is an ArrayBuffer.
+ */
+let isArrayBuffer = function(obj) {
+  return obj != null && typeof obj == "object" &&
+    obj.constructor.name == "ArrayBuffer";
+};
+exports.isArrayBuffer = isArrayBuffer;
+
+/**
  * A |Type| of pointers.
  *
  * @param {string} name The name of this type.
  * @param {CType} implementation The type of this pointer.
  * @param {Type} targetType The target type.
  */
 function PtrType(name, implementation, targetType) {
   Type.call(this, name, implementation);
@@ -425,23 +455,26 @@ PtrType.prototype = Object.create(Type.p
  */
 PtrType.prototype.toMsg = function ptr_toMsg(value) {
   if (value == null) {
     return null;
   }
   if (typeof value == "string") {
     return { string: value };
   }
+  if (isTypedArray(value)) {
+    // Automatically transfer typed arrays
+    return new Meta({data: value}, {transfers: [value.buffer]});
+  }
+  if (isArrayBuffer(value)) {
+    // Automatically transfer array buffers
+    return new Meta({data: value}, {transfers: [value]});
+  }
   let normalized;
-  if (isTypedArray(value)) { // Typed array
-    normalized = Type.uint8_t.in_ptr.implementation(value.buffer);
-    if (value.byteOffset != 0) {
-      normalized = offsetBy(normalized, value.byteOffset);
-    }
-  } else if ("addressOfElement" in value) { // C array
+  if ("addressOfElement" in value) { // C array
     normalized = value.addressOfElement(0);
   } else if ("isNull" in value) { // C pointer
     normalized = value;
   } else {
     throw new TypeError("Value " + value +
       " cannot be converted to a pointer");
   }
   let cast = Type.uintptr_t.cast(normalized);
@@ -453,16 +486,19 @@ PtrType.prototype.toMsg = function ptr_t
  */
 PtrType.prototype.fromMsg = function ptr_fromMsg(msg) {
   if (msg == null) {
     return null;
   }
   if ("string" in msg) {
     return msg.string;
   }
+  if ("data" in msg) {
+    return msg.data;
+  }
   if ("ptr" in msg) {
     let address = ctypes.uintptr_t(msg.ptr);
     return this.cast(address);
   }
   throw new TypeError("Message " + msg.toSource() +
     " does not represent a pointer");
 };
 
@@ -1139,106 +1175,44 @@ function declareLazy(object, field, lib,
         return undefined;
       }
     },
     configurable: true
   });
 }
 exports.declareLazy = declareLazy;
 
-// A bogus array type used to perform pointer arithmetics
-let gOffsetByType;
-
 /**
- * Advance a pointer by a number of items.
- *
- * This method implements adding an integer to a pointer in C.
+ * Utility function used to sanity check buffer and length arguments.  The
+ * buffer must be a Typed Array.
  *
- * Example:
- *   // ptr is a uint16_t*,
- *   offsetBy(ptr, 3)
- *  // returns a uint16_t* with the address ptr + 3 * 2 bytes
+ * @param {Typed array} candidate The buffer.
+ * @param {number} bytes The number of bytes that |candidate| should contain.
  *
- * @param {C pointer} pointer The start pointer.
- * @param {number} length The number of items to advance. Must not be
- * negative.
- *
- * @return {C pointer} |pointer| advanced by |length| items
+ * @return number The bytes argument clamped to the length of the buffer.
  */
-let offsetBy =
-  function offsetBy(pointer, length) {
-    if (length === undefined || length < 0) {
-      throw new TypeError("offsetBy expects a positive number");
-    }
-   if (!("isNull" in pointer)) {
-      throw new TypeError("offsetBy expects a pointer");
-    }
-    if (length == 0) {
-      return pointer;
-    }
-    let type = pointer.constructor;
-    let size = type.targetType.size;
-    if (size == 0 || size == null) {
-      throw new TypeError("offsetBy cannot be applied to a pointer without size");
-    }
-    let bytes = length * size;
-    if (!gOffsetByType || gOffsetByType.size <= bytes) {
-      gOffsetByType = ctypes.uint8_t.array(bytes * 2);
-    }
-    let addr = ctypes.cast(pointer, gOffsetByType.ptr).
-      contents.addressOfElement(bytes);
-    return ctypes.cast(addr, type);
+function normalizeBufferArgs(candidate, bytes) {
+  if (!candidate) {
+    throw new TypeError("Expecting a Typed Array");
+  }
+  if (!isTypedArray(candidate)) {
+    throw new TypeError("Expecting a Typed Array");
+  }
+  if (bytes == null) {
+    bytes = candidate.byteLength;
+  } else if (candidate.byteLength < bytes) {
+    throw new TypeError("Buffer is too short. I need at least " +
+                       bytes +
+                       " bytes but I have only " +
+                       candidate.byteLength +
+                        "bytes");
+  }
+  return bytes;
 };
-exports.offsetBy = offsetBy;
-
-/**
- * Utility function used to normalize a Typed Array or C
- * pointer into a uint8_t C pointer.
- *
- * Future versions might extend this to other data structures.
- *
- * @param {Typed array | C pointer} candidate The buffer. If
- * a C pointer, it must be non-null.
- * @param {number} bytes The number of bytes that |candidate| should contain.
- * Used for sanity checking if the size of |candidate| can be determined.
- *
- * @return {ptr:{C pointer}, bytes:number} A C pointer of type uint8_t,
- * corresponding to the start of |candidate|.
- */
-function normalizeToPointer(candidate, bytes) {
-  if (!candidate) {
-    throw new TypeError("Expecting  a Typed Array or a C pointer");
-  }
-  let ptr;
-  if ("isNull" in candidate) {
-    if (candidate.isNull()) {
-      throw new TypeError("Expecting a non-null pointer");
-    }
-    ptr = Type.uint8_t.out_ptr.cast(candidate);
-    if (bytes == null) {
-      throw new TypeError("C pointer missing bytes indication.");
-    }
-  } else if (isTypedArray(candidate)) {
-    // Typed Array
-    ptr = Type.uint8_t.out_ptr.implementation(candidate.buffer);
-    if (bytes == null) {
-      bytes = candidate.byteLength;
-    } else if (candidate.byteLength < bytes) {
-      throw new TypeError("Buffer is too short. I need at least " +
-                         bytes +
-                         " bytes but I have only " +
-                         candidate.byteLength +
-                          "bytes");
-    }
-  } else {
-    throw new TypeError("Expecting  a Typed Array or a C pointer");
-  }
-  return {ptr: ptr, bytes: bytes};
-};
-exports.normalizeToPointer = normalizeToPointer;
+exports.normalizeBufferArgs = normalizeBufferArgs;
 
 ///////////////////// OS interactions
 
 /**
  * An OS error.
  *
  * This class is provided mostly for type-matching. If you need more
  * details about an error, you should use the platform-specific error
@@ -1269,18 +1243,17 @@ exports.OS = {
     LOG: LOG,
     clone: clone,
     Type: Type,
     HollowStructure: HollowStructure,
     Error: OSError,
     declareFFI: declareFFI,
     projectValue: projectValue,
     isTypedArray: isTypedArray,
-    defineLazyGetter: defineLazyGetter,
-    offsetBy: offsetBy
+    defineLazyGetter: defineLazyGetter
   }
 };
 
 Object.defineProperty(exports.OS.Shared, "DEBUG", {
   get: function() {
     return Config.DEBUG;
   },
   set: function(x) {
--- a/toolkit/components/osfile/modules/osfile_shared_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_shared_front.jsm
@@ -57,66 +57,64 @@ AbstractFile.prototype = {
    * @return {Uint8Array} An array containing the bytes read.
    */
   read: function read(maybeBytes, options = {}) {
     if (typeof maybeBytes === "object") {
     // Caller has skipped `maybeBytes` and provided an options object.
       options = clone(maybeBytes);
       maybeBytes = null;
     } else {
-      options = clone(options || {});
+      options = options || {};
     }
-    if(!("bytes" in options)) {
-      options.bytes = maybeBytes == null ? this.stat().size : maybeBytes;
+    let bytes = options.bytes || undefined;
+    if (bytes === undefined) {
+      bytes = maybeBytes == null ? this.stat().size : maybeBytes;
     }
-    let buffer = new Uint8Array(options.bytes);
-    let {ptr, bytes} = SharedAll.normalizeToPointer(buffer, options.bytes);
+    let buffer = new Uint8Array(bytes);
     let pos = 0;
     while (pos < bytes) {
-      let chunkSize = this._read(ptr, bytes - pos, options);
+      let length = bytes - pos;
+      let view = new DataView(buffer.buffer, pos, length);
+      let chunkSize = this._read(view, length, options);
       if (chunkSize == 0) {
         break;
       }
       pos += chunkSize;
-      ptr = SharedAll.offsetBy(ptr, chunkSize);
     }
-    if (pos == options.bytes) {
+    if (pos == bytes) {
       return buffer;
     } else {
       return buffer.subarray(0, 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 {Typed array | C pointer} buffer The buffer in which the
-   * the bytes are stored. The buffer must be large enough to
-   * accomodate |bytes| bytes.
+   * @param {Typed array} buffer The buffer in which the the bytes are
+   * stored. The buffer must be large enough to accomodate |bytes| bytes.
    * @param {*=} options Optionally, an object that may contain the
    * following fields:
    * - {number} bytes The number of |bytes| to write from the buffer. If
-   * unspecified, this is |buffer.byteLength|. Note that |bytes| is required
-   * if |buffer| is a C pointer.
+   * unspecified, this is |buffer.byteLength|.
    *
    * @return {number} The number of bytes actually written.
    */
   write: function write(buffer, options = {}) {
-
-    let {ptr, bytes} =
-      SharedAll.normalizeToPointer(buffer, ("bytes" in options) ? options.bytes : undefined);
-
+    let bytes =
+      SharedAll.normalizeBufferArgs(buffer, ("bytes" in options) ? options.bytes : undefined);
     let pos = 0;
     while (pos < bytes) {
-      let chunkSize = this._write(ptr, bytes - pos, options);
+      let length = bytes - pos;
+      let view = new DataView(buffer.buffer, buffer.byteOffset + pos, length);
+      let chunkSize = this._write(view, length, options);
       pos += chunkSize;
-      ptr = SharedAll.offsetBy(ptr, chunkSize);
     }
     return pos;
   }
 };
 
 /**
  * Creates and opens a file with a unique name. By default, generate a random HEX number and use it to create a unique new file name.
  *
--- a/toolkit/components/osfile/modules/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_unix_front.jsm
@@ -107,17 +107,17 @@
          UnixFile.read(this.fd, buffer, nbytes),
          this._path
        );
      };
 
      /**
       * Write some bytes to a file.
       *
-      * @param {C pointer} buffer A buffer holding the data that must be
+      * @param {Typed array} buffer A buffer holding the data that must be
       * written.
       * @param {number} nbytes The number of bytes to write. It must not
       * exceed the size of |buffer| in bytes.
       * @param {*=} options Additional options for writing. Ignored in
       * this implementation.
       *
       * @return {number} The number of bytes effectively written.
       * @throws {OS.File.Error} In case of I/O error.
--- a/toolkit/components/osfile/modules/osfile_win_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_front.jsm
@@ -120,17 +120,17 @@
          this._path
        );
        return gBytesRead.value;
      };
 
      /**
       * Write some bytes to a file.
       *
-      * @param {C pointer} buffer A buffer holding the data that must be
+      * @param {Typed array} buffer A buffer holding the data that must be
       * written.
       * @param {number} nbytes The number of bytes to write. It must not
       * exceed the size of |buffer| in bytes.
       * @param {*=} options Additional options for writing. Ignored in
       * this implementation.
       *
       * @return {number} The number of bytes effectively written.
       * @throws {OS.File.Error} In case of I/O error.
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_comms.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_comms.js
@@ -31,20 +31,18 @@ self.onmessage = function(msg) {
                 let view = new Uint8Array(15);
                 for (let i = 0; i < 15; ++i) {
                   view[i] = i;
                 }
                 return view;
               })(),
       type: OS.Shared.Type.char.in_ptr,
       check: function check_ArrayBuffer(candidate, prefix) {
-        let cast = ctypes.cast(candidate, ctypes.uint8_t.ptr);
         for (let i = 0; i < 15; ++i) {
-          is(cast.contents, i % 256, prefix + "Checking that the contents of the ArrayBuffer were preserved");
-          cast = cast.increment();
+          is(candidate[i], i % 256, prefix + "Checking that the contents of the ArrayBuffer were preserved");
         }
       }},
     { typename: "OS.Shared.Type.char.in_ptr",
       valuedescr: "Pointer",
       value: new OS.Shared.Type.char.in_ptr.implementation(1),
       type: OS.Shared.Type.char.in_ptr,
       check: function check_ptr(candidate, prefix) {
         let address = ctypes.cast(candidate, ctypes.uintptr_t).value.toString();
@@ -101,31 +99,37 @@ self.onmessage = function(msg) {
       exn = ex;
     }
     is(exn, null, "Can I serialize the following value? " + value +
       " aka " + JSON.stringify(value));
     if (exn) {
       return;
     }
 
+    if ("data" in serialized) {
+      // Unwrap from `Meta`
+      serialized = serialized.data;
+    }
+
     // 2. Test deserialization
     let deserialized;
     try {
       deserialized = type.fromMsg(serialized);
     } catch (ex) {
       exn = ex;
     }
     is(exn, null, "Can I deserialize the following message? " + serialized
      + " aka " + JSON.stringify(serialized));
     if (exn) {
       return;
     }
 
     // 3. Local test deserialized value
-    info("Running test on deserialized value " + serialized);
+    info("Running test on deserialized value " + deserialized +
+      " aka " + JSON.stringify(deserialized));
     check(deserialized, "Local test: ");
 
     // 4. Test sending serialized
     info("Attempting to send message");
     try {
       self.postMessage({kind:"value",
         typename: sample.typename,
         value: serialized,
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -17,17 +17,16 @@ function should_throw(f) {
 }
 
 self.onmessage = function onmessage_start(msg) {
   self.onmessage = function onmessage_ignored(msg) {
     log("ignored message " + JSON.stringify(msg.data));
   };
   try {
     test_init();
-    test_offsetby();
     test_open_existing_file();
     test_open_non_existing_file();
     test_flush_open_file();
     test_copy_existing_file();
     test_position();
     test_move_file();
     test_iter_dir();
     test_info();
@@ -43,68 +42,16 @@ self.onmessage = function onmessage_star
   finish();
 };
 
 function test_init() {
   info("Starting test_init");
   importScripts("resource://gre/modules/osfile.jsm");
 }
 
-function test_offsetby() {
-  info("Starting test_offsetby");
-
-  // Initialize one array
-  let LENGTH = 1024;
-  let buf = new ArrayBuffer(LENGTH);
-  let view = new Uint8Array(buf);
-  let i;
-  for (i = 0; i < LENGTH; ++i) {
-    view[i] = i;
-  }
-
-  // Walk through the array with offsetBy by 8 bits
-  let uint8 = SharedAll.Type.uint8_t.in_ptr.implementation(buf);
-  for (i = 0; i < LENGTH; ++i) {
-    let value = SharedAll.offsetBy(uint8, i).contents;
-    if (value != i%256) {
-      is(value, i % 256, "test_offsetby: Walking through array with offsetBy (8 bits)");
-      break;
-    }
-  }
-
-  // Walk again by 16 bits
-  let uint16 = SharedAll.Type.uint16_t.in_ptr.implementation(buf);
-  let view2 = new Uint16Array(buf);
-  for (i = 0; i < LENGTH/2; ++i) {
-    let value = SharedAll.offsetBy(uint16, i).contents;
-    if (value != view2[i]) {
-      is(value, view2[i], "test_offsetby: Walking through array with offsetBy (16 bits)");
-      break;
-    }
-  }
-
-  // Ensure that offsetBy(..., 0) is idempotent
-  let startptr = SharedAll.offsetBy(uint8, 0);
-  let startptr2 = SharedAll.offsetBy(startptr, 0);
-  is(startptr.toString(), startptr2.toString(), "test_offsetby: offsetBy(..., 0) is idmpotent");
-
-  // Ensure that offsetBy(ptr, ...) does not work if ptr is a void*
-  let ptr = ctypes.voidptr_t(0);
-  let exn;
-  try {
-    SharedAll.offsetBy(ptr, 1);
-  } catch (x) {
-    exn = x;
-  }
-  ok(!!exn, "test_offsetby: rejected offsetBy with void*");
-
-  info("test_offsetby: complete");
-}
-
-
 /**
  * Test that we can open an existing file.
  */
 function test_open_existing_file()
 {
   info("Starting test_open_existing");
   let file = OS.File.open("chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js");
   file.close();
--- a/toolkit/components/osfile/tests/xpcshell/test_compression.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_compression.js
@@ -7,29 +7,31 @@ Components.utils.import("resource://gre/
 
 function run_test() {
   do_test_pending();
   run_next_test();
 }
 
 add_task(function test_compress_lz4() {
   let path = OS.Path.join(OS.Constants.Path.tmpDir, "compression.lz");
-  let array = new Uint8Array(1024);
+  let length = 1024;
+  let array = new Uint8Array(length);
   for (let i = 0; i < array.byteLength; ++i) {
     array[i] = i;
   }
+  let arrayAsString = Array.prototype.join.call(array);
 
   do_print("Writing data with lz4 compression");
   let bytes = yield OS.File.writeAtomic(path, array, { compression: "lz4" });
-  do_print("Compressed " + array.byteLength + " bytes into " + bytes);
+  do_print("Compressed " + length + " bytes into " + bytes);
 
   do_print("Reading back with lz4 decompression");
   let decompressed = yield OS.File.read(path, { compression: "lz4" });
   do_print("Decompressed into " + decompressed.byteLength + " bytes");
-  do_check_eq(Array.prototype.join.call(array), Array.prototype.join.call(decompressed));
+  do_check_eq(arrayAsString, Array.prototype.join.call(decompressed));
 });
 
 add_task(function test_uncompressed() {
   do_print("Writing data without compression");
   let path = OS.Path.join(OS.Constants.Path.tmpDir, "no_compression.tmp");
   let array = new Uint8Array(1024);
   for (let i = 0; i < array.byteLength; ++i) {
     array[i] = i;
--- a/toolkit/components/osfile/tests/xpcshell/test_read_write.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_read_write.js
@@ -36,29 +36,31 @@ add_test_pair(function* read_write_all()
 
       let optionsBackup = JSON.parse(JSON.stringify(options));
 
       // Check that read + writeAtomic performs a correct copy
       let currentDir = yield OS.File.getCurrentDirectory();
       let pathSource = OS.Path.join(currentDir, EXISTING_FILE);
       let contents = yield OS.File.read(pathSource);
       do_check_true(!!contents); // Content is not empty
+      let bytesRead = contents.byteLength;
 
       let bytesWritten = yield OS.File.writeAtomic(DEST_PATH, contents, options);
-      do_check_eq(contents.byteLength, bytesWritten); // Correct number of bytes written
+      do_check_eq(bytesRead, bytesWritten); // Correct number of bytes written
 
       // Check that options are not altered
       do_check_eq(JSON.stringify(options), JSON.stringify(optionsBackup));
       yield reference_compare_files(pathSource, DEST_PATH, TEST);
 
       // Check that temporary file was removed or never created exist
       do_check_false(new FileUtils.File(TMP_PATH).exists());
 
       // Check that writeAtomic fails if noOverwrite is true and the destination
       // file already exists!
+      contents = new Uint8Array(300);
       let view = new Uint8Array(contents.buffer, 10, 200);
       try {
         let opt = JSON.parse(JSON.stringify(options));
         opt.noOverwrite = true;
         yield OS.File.writeAtomic(DEST_PATH, view, opt);
         do_throw("With noOverwrite, writeAtomic should have refused to overwrite file (" + suffix + ")");
       } catch (err if err instanceof OS.File.Error && err.becauseExists) {
         do_print("With noOverwrite, writeAtomic correctly failed (" + suffix + ")");
@@ -66,26 +68,27 @@ add_test_pair(function* read_write_all()
       yield reference_compare_files(pathSource, DEST_PATH, TEST);
 
       // Check that temporary file was removed or never created
       do_check_false(new FileUtils.File(TMP_PATH).exists());
 
       // Now write a subset
       let START = 10;
       let LENGTH = 100;
+      contents = new Uint8Array(300);
+      for (var i = 0; i < contents.byteLength; i++)
+        contents[i] = i % 256;
       view = new Uint8Array(contents.buffer, START, LENGTH);
       bytesWritten = yield OS.File.writeAtomic(DEST_PATH, view, options);
       do_check_eq(bytesWritten, LENGTH);
 
       let array2 = yield OS.File.read(DEST_PATH);
-      let view1 = new Uint8Array(contents.buffer, START, LENGTH);
-      do_check_eq(view1.length, array2.length);
-      let decoder = new TextDecoder();
-      do_check_eq(decoder.decode(view1), decoder.decode(array2));
-
+      do_check_eq(LENGTH, array2.length);
+      for (var i = 0; i < LENGTH; i++)
+        do_check_eq(array2[i], (i + START) % 256);
 
       // Cleanup.
       yield OS.File.remove(DEST_PATH);
       yield OS.File.remove(TMP_PATH);
     });
   };
 
   yield test_with_options({tmpPath: TMP_PATH}, "Renaming, not flushing");
--- a/toolkit/components/workerlz4/lz4.js
+++ b/toolkit/components/workerlz4/lz4.js
@@ -88,49 +88,49 @@ function compressFileContent(array, opti
   let view = new DataView(outputArray.buffer);
   view.setUint32(MAGIC_NUMBER.byteLength, inputBytes, true);
 
   return new Uint8Array(outputArray.buffer, 0, HEADER_SIZE + compressedSize);
 }
 exports.compressFileContent = compressFileContent;
 
 function decompressFileContent(array, options = {}) {
-  let {ptr, bytes} = SharedAll.normalizeToPointer(array, options.bytes || null);
+  let bytes = SharedAll.normalizeBufferArgs(array, options.bytes || null);
   if (bytes < HEADER_SIZE) {
     throw new LZError("decompress", "becauseLZNoHeader", "Buffer is too short (no header)");
   }
 
   // Read headers
-  let expectMagicNumber = ctypes.cast(ptr, EXPECTED_HEADER_TYPE.ptr).contents;
+  let expectMagicNumber = new DataView(array.buffer, 0, MAGIC_NUMBER.byteLength);
   for (let i = 0; i < MAGIC_NUMBER.byteLength; ++i) {
-    if (expectMagicNumber[i] != MAGIC_NUMBER[i]) {
+    if (expectMagicNumber.getUint8(i) != MAGIC_NUMBER[i]) {
       throw new LZError("decompress", "becauseLZWrongMagicNumber", "Invalid header (no magic number");
     }
   }
 
-  let sizeBuf =
-    ctypes.cast(
-      SharedAll.offsetBy(ptr, MAGIC_NUMBER.byteLength),
-      EXPECTED_SIZE_BUFFER_TYPE.ptr).contents;
+  let sizeBuf = new DataView(array.buffer, MAGIC_NUMBER.byteLength, BYTES_IN_SIZE_HEADER);
   let expectDecompressedSize =
-    sizeBuf[0] + (sizeBuf[1] << 8) + (sizeBuf[2] << 16) + (sizeBuf[3] << 24);
+    sizeBuf.getUint8(0) +
+    (sizeBuf.getUint8(1) << 8) +
+    (sizeBuf.getUint8(2) << 16) +
+    (sizeBuf.getUint8(3) << 24);
   if (expectDecompressedSize == 0) {
     // The underlying algorithm cannot handle a size of 0
     return new Uint8Array(0);
   }
 
   // Prepare the input buffer
-  let inputPtr = SharedAll.offsetBy(ptr, HEADER_SIZE);
+  let inputData = new DataView(array.buffer, HEADER_SIZE);
 
   // Prepare the output buffer
   let outputBuffer = new Uint8Array(expectDecompressedSize);
   let decompressedBytes = (new SharedAll.Type.size_t.implementation(0));
 
   // Decompress
-  let success = Internals.decompress(inputPtr, bytes - HEADER_SIZE,
+  let success = Internals.decompress(inputData, bytes - HEADER_SIZE,
                                      outputBuffer, outputBuffer.byteLength,
                                      decompressedBytes.address());
   if (!success) {
     throw new LZError("decompress", "becauseLZInvalidContent", "Invalid content:Decompression stopped at " + decompressedBytes.value);
   }
   return new Uint8Array(outputBuffer.buffer, outputBuffer.byteOffset, decompressedBytes.value);
 }
 exports.decompressFileContent = decompressFileContent;
--- a/toolkit/modules/ZipUtils.jsm
+++ b/toolkit/modules/ZipUtils.jsm
@@ -47,17 +47,16 @@ function saveStreamAsync(aPath, aStream,
             getService(Ci.nsIStreamTransportService);
   let transport = sts.createInputTransport(aStream, -1, -1, true);
   let input = transport.openInputStream(0, 0, 0)
                        .QueryInterface(Ci.nsIAsyncInputStream);
   let source = Cc["@mozilla.org/binaryinputstream;1"].
                createInstance(Ci.nsIBinaryInputStream);
   source.setInputStream(input);
 
-  let data = new Uint8Array(EXTRACTION_BUFFER);
 
   function readFailed(error) {
     try {
       aStream.close();
     }
     catch (e) {
       logger.error("Failed to close JAR stream for " + aPath);
     }
@@ -67,17 +66,18 @@ function saveStreamAsync(aPath, aStream,
     }, function(e) {
       logger.error("Failed to close file for " + aPath);
       deferred.reject(error);
     });
   }
 
   function readData() {
     try {
-      let count = Math.min(source.available(), data.byteLength);
+      let count = Math.min(source.available(), EXTRACTION_BUFFER);
+      let data = new Uint8Array(count);
       source.readArrayBuffer(count, data.buffer);
 
       aFile.write(data, { bytes: count }).then(function() {
         input.asyncWait(readData, 0, 0, Services.tm.currentThread);
       }, readFailed);
     }
     catch (e if e.result == Cr.NS_BASE_STREAM_CLOSED) {
       deferred.resolve(aFile.close());