Add support for compression filters in the mojo dart:io

Fixes #691

- Add native implementation of zlib filters from standalone VM.
- Add filters apptest.

R=zra@google.com

Review URL: https://codereview.chromium.org/1760523007 .
diff --git a/mojo/dart/apptests/dart_apptests/BUILD.gn b/mojo/dart/apptests/dart_apptests/BUILD.gn
index b6c17ca..05ff010 100644
--- a/mojo/dart/apptests/dart_apptests/BUILD.gn
+++ b/mojo/dart/apptests/dart_apptests/BUILD.gn
@@ -13,6 +13,7 @@
     "lib/src/connect_to_loader_apptests.dart",
     "lib/src/echo_apptests.dart",
     "lib/src/file_apptests.dart",
+    "lib/src/filter_apptests.dart",
     "lib/src/io_http_apptests.dart",
     "lib/src/io_internet_address_apptests.dart",
     "lib/src/pingpong_apptests.dart",
diff --git a/mojo/dart/apptests/dart_apptests/lib/main.dart b/mojo/dart/apptests/dart_apptests/lib/main.dart
index 5922ef6..d8f024e 100644
--- a/mojo/dart/apptests/dart_apptests/lib/main.dart
+++ b/mojo/dart/apptests/dart_apptests/lib/main.dart
@@ -8,6 +8,7 @@
 import 'src/connect_to_loader_apptests.dart' as connect_to_loader_apptests;
 import 'src/echo_apptests.dart' as echo;
 import 'src/file_apptests.dart' as file;
+import 'src/filter_apptests.dart' as filter;
 import 'src/io_http_apptests.dart' as io_http;
 import 'src/io_internet_address_apptests.dart' as io_internet_address;
 import 'src/pingpong_apptests.dart' as pingpong;
@@ -20,6 +21,7 @@
     connect_to_loader_apptests.connectToLoaderApptests,
     echo.echoApptests,
     file.tests,
+    filter.tests,
     io_internet_address.tests,
     io_http.tests,
     pingpong.pingpongApptests,
diff --git a/mojo/dart/apptests/dart_apptests/lib/src/filter_apptests.dart b/mojo/dart/apptests/dart_apptests/lib/src/filter_apptests.dart
new file mode 100644
index 0000000..546b712
--- /dev/null
+++ b/mojo/dart/apptests/dart_apptests/lib/src/filter_apptests.dart
@@ -0,0 +1,149 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library filter_apptests;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:mojo_apptest/apptest.dart';
+import 'package:mojo/application.dart';
+import 'package:mojo/bindings.dart';
+import 'package:mojo/core.dart';
+
+var generateListTypes = [
+  (list) => list,
+  (list) => new Uint8List.fromList(list),
+  (list) => new Int8List.fromList(list),
+  (list) => new Uint16List.fromList(list),
+  (list) => new Int16List.fromList(list),
+  (list) => new Uint32List.fromList(list),
+  (list) => new Int32List.fromList(list),
+];
+
+var generateViewTypes = [
+  (list) => new Uint8List.view((new Uint8List.fromList(list)).buffer, 1, 8),
+  (list) => new Int8List.view((new Int8List.fromList(list)).buffer, 1, 8),
+  (list) => new Uint16List.view((new Uint16List.fromList(list)).buffer, 2, 6),
+  (list) => new Int16List.view((new Int16List.fromList(list)).buffer, 2, 6),
+  (list) => new Uint32List.view((new Uint32List.fromList(list)).buffer, 4, 4),
+  (list) => new Int32List.view((new Int32List.fromList(list)).buffer, 4, 4),
+];
+
+void testZLibInflateSync(List<int> data) {
+  [true, false].forEach((gzip) {
+    [3, 6, 9].forEach((level) {
+      var encoded = new ZLibEncoder(gzip: gzip, level: level).convert(data);
+      var decoded = new ZLibDecoder().convert(encoded);
+      expect(decoded, equals(data));
+    });
+  });
+}
+
+tests(Application application, String url) {
+  group('Filter Apptests', () {
+    test('ZLibDeflateEmpty', () async {
+      Completer completer = new Completer();
+      var controller = new StreamController(sync: true);
+      controller.stream.transform(new ZLibEncoder(gzip: false, level: 6))
+          .fold([], (buffer, data) {
+            buffer.addAll(data);
+            return buffer;
+          })
+          .then((data) {
+            expect(data, equals([120, 156, 3, 0, 0, 0, 0, 1]));
+            completer.complete(null);
+          });
+      controller.close();
+      await completer.future;
+    });
+    test('ZLibDeflateEmptyGzip', () async {
+      Completer completer = new Completer();
+      var controller = new StreamController(sync: true);
+      controller.stream.transform(new ZLibEncoder(gzip: true, level: 6))
+          .fold([], (buffer, data) {
+            buffer.addAll(data);
+            return buffer;
+          })
+          .then((data) {
+            expect(data.length > 0, isTrue);
+            expect([], equals(new ZLibDecoder().convert(data)));
+            completer.complete(null);
+          });
+      controller.close();
+      await completer.future;
+    });
+    test('ZlibInflateThrowsWithSmallerWindow', () async {
+      var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+      var encoder = new ZLibEncoder(windowBits: 10);
+      var encodedData = encoder.convert(data);
+      var decoder = new ZLibDecoder(windowBits: 8);
+      bool caughtException = false;
+      try {
+        decoder.convert(encodedData);
+        expect(true, isFalse);
+      } catch (e) {
+        caughtException = true;
+      }
+      expect(caughtException, isTrue);
+    });
+    test('ZlibInflateWithLargerWindow', () async {
+      var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+      Completer completer = new Completer();
+
+      int doneCount = 0;
+      const totalCount = 6;  // [true, false] * [3, 6, 9].
+
+      [true, false].forEach((gzip) {
+        [3, 6, 9].forEach((level) {
+          var controller = new StreamController(sync: true);
+          controller.stream
+              .transform(new ZLibEncoder(gzip: gzip, level: level,
+                                         windowBits: 8))
+              .transform(new ZLibDecoder(windowBits: 10))
+              .fold([], (buffer, data) {
+                buffer.addAll(data);
+                return buffer;
+              })
+              .then((inflated) {
+                expect(inflated, equals(data));
+                doneCount++;
+                if (doneCount == totalCount) {
+                  completer.complete(null);
+                }
+              });
+          controller.add(data);
+          controller.close();
+        });
+      });
+      await completer.future;
+    });
+    test('ZlibWithDictionary', () async {
+      var dict = [102, 111, 111, 98, 97, 114];
+      var data = [98, 97, 114, 102, 111, 111];
+
+      [3, 6, 9].forEach((level) {
+        var encoded = new ZLibEncoder(level: level, dictionary: dict)
+            .convert(data);
+        var decoded = new ZLibDecoder(dictionary: dict).convert(encoded);
+        expect(decoded, equals(data));
+      });
+    });
+    test('List Types', () async {
+      generateListTypes.forEach((f) {
+        var data = f([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+        testZLibInflateSync(data);
+      });
+    });
+    test('List View Types', () async {
+      generateViewTypes.forEach((f) {
+        var data = f([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+        testZLibInflateSync(data);
+      });
+    });
+  });
+}
diff --git a/mojo/dart/embedder/BUILD.gn b/mojo/dart/embedder/BUILD.gn
index d47a349..97f848d 100644
--- a/mojo/dart/embedder/BUILD.gn
+++ b/mojo/dart/embedder/BUILD.gn
@@ -26,6 +26,8 @@
     "common.h",
     "dart_controller.cc",
     "dart_controller.h",
+    "io/filter.cc",
+    "io/filter.h",
     "io/internet_address.h",
     "io/internet_address_posix.cc",
     "mojo_dart_state.h",
@@ -51,6 +53,7 @@
     "//mojo/public/platform/dart:mojo_internal_impl",
     "//mojo/services/files/interfaces",
     "//mojo/services/network/interfaces",
+    "//third_party/zlib",
     "//tonic",
   ]
 
@@ -66,6 +69,7 @@
   inputs = [
     "//mojo/dart/embedder/core/natives_patch.dart",
     "//mojo/dart/embedder/io/file_patch.dart",
+    "//mojo/dart/embedder/io/filter_patch.dart",
     "//mojo/dart/embedder/io/internet_address_patch.dart",
     "//mojo/dart/embedder/io/mojo_patch.dart",
     "//mojo/dart/embedder/io/platform_patch.dart",
diff --git a/mojo/dart/embedder/builtin.cc b/mojo/dart/embedder/builtin.cc
index c47781f..38b89dd 100644
--- a/mojo/dart/embedder/builtin.cc
+++ b/mojo/dart/embedder/builtin.cc
@@ -43,6 +43,7 @@
 
 const char* Builtin::mojo_io_patch_resource_names_[] = {
     "/io/file_patch.dart",
+    "/io/filter_patch.dart",
     "/io/internet_address_patch.dart",
     "/io/mojo_patch.dart",
     "/io/platform_patch.dart",
diff --git a/mojo/dart/embedder/common.cc b/mojo/dart/embedder/common.cc
index ec5f3db..fe3eeb8 100644
--- a/mojo/dart/embedder/common.cc
+++ b/mojo/dart/embedder/common.cc
@@ -5,12 +5,39 @@
 #include <stdint.h>
 #include <string.h>
 
+#include "base/logging.h"
 #include "mojo/dart/embedder/common.h"
 #include "mojo/public/cpp/environment/logging.h"
 
 namespace mojo {
 namespace dart {
 
+Dart_Handle DartEmbedder::GetDartType(const char* library_url,
+                                      const char* class_name) {
+  return Dart_GetType(Dart_LookupLibrary(NewCString(library_url)),
+                      NewCString(class_name), 0, NULL);
+}
+
+Dart_Handle DartEmbedder::NewDartExceptionWithMessage(
+    const char* library_url,
+    const char* error_type,
+    const char* message) {
+  // Create a Dart Exception object with a message.
+  Dart_Handle type = GetDartType(library_url, error_type);
+  DCHECK(!Dart_IsError(type));
+  if (message != NULL) {
+    Dart_Handle args[1];
+    args[0] = NewCString(message);
+    return Dart_New(type, Dart_Null(), 1, args);
+  } else {
+    return Dart_New(type, Dart_Null(), 0, NULL);
+  }
+}
+
+Dart_Handle DartEmbedder::NewInternalError(const char* message) {
+  return NewDartExceptionWithMessage("dart:core", "_InternalError", message);
+}
+
 int64_t DartEmbedder::GetIntegerValue(Dart_Handle value_obj) {
   int64_t value = 0;
   Dart_Handle result = Dart_IntegerToInt64(value_obj, &value);
diff --git a/mojo/dart/embedder/common.h b/mojo/dart/embedder/common.h
index 526c048..136708d 100644
--- a/mojo/dart/embedder/common.h
+++ b/mojo/dart/embedder/common.h
@@ -12,6 +12,15 @@
 
 class DartEmbedder {
  public:
+  static Dart_Handle GetDartType(const char* library_url,
+                                 const char* class_name);
+
+  static Dart_Handle NewDartExceptionWithMessage(const char* library_url,
+                                                 const char* error_type,
+                                                 const char* message);
+
+  static Dart_Handle NewInternalError(const char* message);
+
   // Returns the integer value of a Dart object. If the object is not
   // an integer value an API error is propagated.
   static int64_t GetIntegerValue(Dart_Handle value_obj);
diff --git a/mojo/dart/embedder/io/filter.cc b/mojo/dart/embedder/io/filter.cc
new file mode 100644
index 0000000..b4c0eda
--- /dev/null
+++ b/mojo/dart/embedder/io/filter.cc
@@ -0,0 +1,191 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/dart/embedder/io/filter.h"
+
+namespace mojo {
+namespace dart {
+
+const int kZLibFlagUseGZipHeader = 16;
+const int kZLibFlagAcceptAnyHeader = 32;
+
+static const int kFilterPointerNativeField = 0;
+
+Dart_Handle Filter::SetFilterPointerNativeField(Dart_Handle filter,
+                                                Filter* filter_pointer) {
+  return Dart_SetNativeInstanceField(
+      filter,
+      kFilterPointerNativeField,
+      reinterpret_cast<intptr_t>(filter_pointer));
+}
+
+
+Dart_Handle Filter::GetFilterPointerNativeField(Dart_Handle filter,
+                                                Filter** filter_pointer) {
+  return Dart_GetNativeInstanceField(
+      filter,
+      kFilterPointerNativeField,
+      reinterpret_cast<intptr_t*>(filter_pointer));
+}
+
+
+ZLibDeflateFilter::~ZLibDeflateFilter() {
+  delete[] dictionary_;
+  delete[] current_buffer_;
+  if (initialized()) deflateEnd(&stream_);
+}
+
+
+bool ZLibDeflateFilter::Init() {
+  int window_bits = window_bits_;
+  if (raw_) {
+    window_bits = -window_bits;
+  } else if (gzip_) {
+    window_bits += kZLibFlagUseGZipHeader;
+  }
+  stream_.next_in = Z_NULL;
+  stream_.zalloc = Z_NULL;
+  stream_.zfree = Z_NULL;
+  stream_.opaque = Z_NULL;
+  int result = deflateInit2(&stream_, level_, Z_DEFLATED, window_bits,
+                            mem_level_, strategy_);
+  if (result != Z_OK) {
+    return false;
+  }
+  if (dictionary_ != NULL && !gzip_ && !raw_) {
+    result = deflateSetDictionary(&stream_, dictionary_, dictionary_length_);
+    delete[] dictionary_;
+    dictionary_ = NULL;
+    if (result != Z_OK) {
+      return false;
+    }
+  }
+  set_initialized(true);
+  return true;
+}
+
+
+bool ZLibDeflateFilter::Process(uint8_t* data, intptr_t length) {
+  if (current_buffer_ != NULL) return false;
+  stream_.avail_in = length;
+  stream_.next_in = current_buffer_ = data;
+  return true;
+}
+
+intptr_t ZLibDeflateFilter::Processed(uint8_t* buffer,
+                                      intptr_t length,
+                                      bool flush,
+                                      bool end) {
+  stream_.avail_out = length;
+  stream_.next_out = buffer;
+  bool error = false;
+  switch (deflate(&stream_,
+                  end ? Z_FINISH : flush ? Z_SYNC_FLUSH : Z_NO_FLUSH)) {
+    case Z_STREAM_END:
+    case Z_BUF_ERROR:
+    case Z_OK: {
+      intptr_t processed = length - stream_.avail_out;
+      if (processed == 0) {
+        break;
+      }
+      return processed;
+    }
+
+    default:
+    case Z_STREAM_ERROR:
+        error = true;
+  }
+
+  delete[] current_buffer_;
+  current_buffer_ = NULL;
+  // Either 0 Byte processed or error
+  return error ? -1 : 0;
+}
+
+
+ZLibInflateFilter::~ZLibInflateFilter() {
+  delete[] dictionary_;
+  delete[] current_buffer_;
+  if (initialized()) inflateEnd(&stream_);
+}
+
+
+bool ZLibInflateFilter::Init() {
+  int window_bits = raw_ ?
+      -window_bits_ :
+      window_bits_ | kZLibFlagAcceptAnyHeader;
+
+  stream_.next_in = Z_NULL;
+  stream_.avail_in = 0;
+  stream_.zalloc = Z_NULL;
+  stream_.zfree = Z_NULL;
+  stream_.opaque = Z_NULL;
+  int result = inflateInit2(&stream_, window_bits);
+  if (result != Z_OK) {
+    return false;
+  }
+  set_initialized(true);
+  return true;
+}
+
+
+bool ZLibInflateFilter::Process(uint8_t* data, intptr_t length) {
+  if (current_buffer_ != NULL) return false;
+  stream_.avail_in = length;
+  stream_.next_in = current_buffer_ = data;
+  return true;
+}
+
+
+intptr_t ZLibInflateFilter::Processed(uint8_t* buffer,
+                                      intptr_t length,
+                                      bool flush,
+                                      bool end) {
+  stream_.avail_out = length;
+  stream_.next_out = buffer;
+  bool error = false;
+  int v;
+  switch (v = inflate(&stream_,
+                  end ? Z_FINISH : flush ? Z_SYNC_FLUSH : Z_NO_FLUSH)) {
+    case Z_STREAM_END:
+    case Z_BUF_ERROR:
+    case Z_OK: {
+      intptr_t processed = length - stream_.avail_out;
+      if (processed == 0) {
+        break;
+      }
+      return processed;
+    }
+
+    case Z_NEED_DICT:
+      if (dictionary_ == NULL) {
+        error = true;
+      } else {
+        int result = inflateSetDictionary(&stream_, dictionary_,
+                                          dictionary_length_);
+        delete[] dictionary_;
+        dictionary_ = NULL;
+        error = result != Z_OK;
+      }
+      if (error) {
+        break;
+      } else {
+        return Processed(buffer, length, flush, end);
+      }
+
+    default:
+    case Z_MEM_ERROR:
+    case Z_DATA_ERROR:
+    case Z_STREAM_ERROR:
+      error = true;
+  }
+
+  delete[] current_buffer_;
+  current_buffer_ = NULL;
+  // Either 0 Byte processed or error
+  return error ? -1 : 0;
+}
+
+}  // namespace dart
+}  // namespace mojo
diff --git a/mojo/dart/embedder/io/filter.h b/mojo/dart/embedder/io/filter.h
new file mode 100644
index 0000000..29db05e
--- /dev/null
+++ b/mojo/dart/embedder/io/filter.h
@@ -0,0 +1,110 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_DART_EMBEDDER_IO_FILTER_H_
+#define MOJO_DART_EMBEDDER_IO_FILTER_H_
+
+#include "dart/runtime/include/dart_api.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "third_party/zlib/zlib.h"
+
+namespace mojo {
+namespace dart {
+
+class Filter {
+ public:
+  virtual ~Filter() {}
+
+  virtual bool Init() = 0;
+
+  /**
+   * On a successful call to Process, Process will take ownership of data. On
+   * successive calls to either Processed or ~Filter, data will be freed with
+   * a delete[] call.
+   */
+  virtual bool Process(uint8_t* data, intptr_t length) = 0;
+  virtual intptr_t Processed(uint8_t* buffer, intptr_t length, bool finish,
+                             bool end) = 0;
+
+  static Dart_Handle SetFilterPointerNativeField(Dart_Handle filter,
+                                                 Filter* filter_pointer);
+  static Dart_Handle GetFilterPointerNativeField(Dart_Handle filter,
+                                                 Filter** filter_pointer);
+
+  bool initialized() const { return initialized_; }
+  void set_initialized(bool value) { initialized_ = value; }
+  uint8_t* processed_buffer() { return processed_buffer_; }
+  intptr_t processed_buffer_size() const { return kFilterBufferSize; }
+
+ protected:
+  Filter() : initialized_(false) {}
+
+ private:
+  static const intptr_t kFilterBufferSize = 65536;
+  uint8_t processed_buffer_[kFilterBufferSize];
+  bool initialized_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Filter);
+};
+
+class ZLibDeflateFilter : public Filter {
+ public:
+  ZLibDeflateFilter(bool gzip, int32_t level, int32_t window_bits,
+                    int32_t mem_level, int32_t strategy,
+                    uint8_t* dictionary, intptr_t dictionary_length, bool raw)
+      : gzip_(gzip), level_(level), window_bits_(window_bits),
+        mem_level_(mem_level), strategy_(strategy), dictionary_(dictionary),
+        dictionary_length_(dictionary_length), raw_(raw), current_buffer_(NULL)
+    {}
+  virtual ~ZLibDeflateFilter();
+
+  virtual bool Init();
+  virtual bool Process(uint8_t* data, intptr_t length);
+  virtual intptr_t Processed(uint8_t* buffer, intptr_t length, bool finish,
+                             bool end);
+
+ private:
+  const bool gzip_;
+  const int32_t level_;
+  const int32_t window_bits_;
+  const int32_t mem_level_;
+  const int32_t strategy_;
+  uint8_t* dictionary_;
+  const intptr_t dictionary_length_;
+  const bool raw_;
+  uint8_t* current_buffer_;
+  z_stream stream_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ZLibDeflateFilter);
+};
+
+class ZLibInflateFilter : public Filter {
+ public:
+  ZLibInflateFilter(int32_t window_bits, uint8_t* dictionary,
+                    intptr_t dictionary_length, bool raw)
+      : window_bits_(window_bits), dictionary_(dictionary),
+        dictionary_length_(dictionary_length), raw_(raw), current_buffer_(NULL)
+    {}
+  virtual ~ZLibInflateFilter();
+
+  virtual bool Init();
+  virtual bool Process(uint8_t* data, intptr_t length);
+  virtual intptr_t Processed(uint8_t* buffer, intptr_t length, bool finish,
+                             bool end);
+
+ private:
+  const int32_t window_bits_;
+  uint8_t* dictionary_;
+  const intptr_t dictionary_length_;
+  const bool raw_;
+  uint8_t* current_buffer_;
+  z_stream stream_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ZLibInflateFilter);
+};
+
+}  // namespace dart
+}  // namespace mojo
+
+#endif  // MOJO_DART_EMBEDDER_IO_FILTER_H_
diff --git a/mojo/dart/embedder/io/filter_patch.dart b/mojo/dart/embedder/io/filter_patch.dart
new file mode 100644
index 0000000..3a32170
--- /dev/null
+++ b/mojo/dart/embedder/io/filter_patch.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+
+class _FilterImpl extends NativeFieldWrapperClass1 implements _Filter {
+  void process(List<int> data, int start, int end) native "Filter_Process";
+
+  List<int> processed({bool flush: true, bool end: false})
+      native "Filter_Processed";
+
+  void end() native "Filter_End";
+}
+
+class _ZLibInflateFilter extends _FilterImpl {
+  _ZLibInflateFilter(int windowBits, List<int> dictionary, bool raw) {
+    _init(windowBits, dictionary, raw);
+  }
+  void _init(int windowBits, List<int> dictionary, bool raw)
+      native "Filter_CreateZLibInflate";
+}
+
+class _ZLibDeflateFilter extends _FilterImpl {
+  _ZLibDeflateFilter(bool gzip, int level, int windowBits, int memLevel,
+                     int strategy, List<int> dictionary, bool raw) {
+    _init(gzip, level, windowBits, memLevel, strategy, dictionary, raw);
+  }
+  void _init(bool gzip, int level, int windowBits, int memLevel,
+             int strategy, List<int> dictionary, bool raw)
+      native "Filter_CreateZLibDeflate";
+}
+
+patch class _Filter {
+  /* patch */ static _Filter _newZLibDeflateFilter(bool gzip, int level,
+                                                   int windowBits, int memLevel,
+                                                   int strategy,
+                                                   List<int> dictionary,
+                                                   bool raw) =>
+      new _ZLibDeflateFilter(gzip, level, windowBits, memLevel, strategy,
+                             dictionary, raw);
+  /* patch */ static _Filter _newZLibInflateFilter(int windowBits,
+                                                   List<int> dictionary,
+                                                   bool raw) =>
+      new _ZLibInflateFilter(windowBits, dictionary, raw);
+}
diff --git a/mojo/dart/embedder/io/internet_address.h b/mojo/dart/embedder/io/internet_address.h
index 2cc1fbf..8a33149 100644
--- a/mojo/dart/embedder/io/internet_address.h
+++ b/mojo/dart/embedder/io/internet_address.h
@@ -45,4 +45,3 @@
 }  // namespace mojo
 
 #endif  // MOJO_DART_EMBEDDER_IO_INTERNET_ADDRESS_H_
-
diff --git a/mojo/dart/embedder/io/mojo_patch.dart b/mojo/dart/embedder/io/mojo_patch.dart
index 3e60bc8..96a62a3 100644
--- a/mojo/dart/embedder/io/mojo_patch.dart
+++ b/mojo/dart/embedder/io/mojo_patch.dart
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'dart:nativewrappers';
+
 import 'dart:_mojo/application.dart';
 import 'dart:_mojo/bindings.dart';
 import 'dart:_mojo/core.dart';
diff --git a/mojo/dart/embedder/mojo_io_natives.cc b/mojo/dart/embedder/mojo_io_natives.cc
index dd18a86..80f283a 100644
--- a/mojo/dart/embedder/mojo_io_natives.cc
+++ b/mojo/dart/embedder/mojo_io_natives.cc
@@ -4,10 +4,12 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <limits>
 
 #include "dart/runtime/include/dart_api.h"
 #include "mojo/dart/embedder/builtin.h"
 #include "mojo/dart/embedder/common.h"
+#include "mojo/dart/embedder/io/filter.h"
 #include "mojo/dart/embedder/io/internet_address.h"
 #include "mojo/dart/embedder/mojo_dart_state.h"
 #include "mojo/public/cpp/system/macros.h"
@@ -16,6 +18,11 @@
 namespace dart {
 
 #define MOJO_IO_NATIVE_LIST(V)                                                 \
+  V(Filter_CreateZLibDeflate, 8)                                               \
+  V(Filter_CreateZLibInflate, 4)                                               \
+  V(Filter_End, 1)                                                             \
+  V(Filter_Process, 4)                                                         \
+  V(Filter_Processed, 3)                                                       \
   V(InternetAddress_Parse, 1)                                                  \
   V(InternetAddress_Reverse, 1)                                                \
   V(Platform_NumberOfProcessors, 0)                                            \
@@ -68,6 +75,266 @@
   return nullptr;
 }
 
+class IOBuffer {
+ public:
+  static Dart_Handle Allocate(intptr_t size, uint8_t **buffer) {
+    uint8_t* data = Allocate(size);
+    Dart_Handle result = Dart_NewExternalTypedData(
+        Dart_TypedData_kUint8, data, size);
+    Dart_NewWeakPersistentHandle(result, data, size, IOBuffer::Finalizer);
+
+    if (Dart_IsError(result)) {
+      Free(data);
+      Dart_PropagateError(result);
+    }
+    if (buffer != NULL) {
+      *buffer = data;
+    }
+    return result;
+  }
+
+  // Allocate IO buffer storage.
+  static uint8_t* Allocate(intptr_t size) {
+    return new uint8_t[size];
+  }
+
+  // Function for disposing of IO buffer storage. All backing storage
+  // for IO buffers must be freed using this function.
+  static void Free(void* buffer) {
+    delete[] reinterpret_cast<uint8_t*>(buffer);
+  }
+
+  // Function for finalizing external byte arrays used as IO buffers.
+  static void Finalizer(void* isolate_callback_data,
+                        Dart_WeakPersistentHandle handle,
+                        void* buffer) {
+    Free(buffer);
+  }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(IOBuffer);
+};
+
+static Filter* GetFilter(Dart_Handle filter_obj) {
+  Filter* filter;
+  Dart_Handle result = Filter::GetFilterPointerNativeField(filter_obj, &filter);
+  if (Dart_IsError(result)) {
+    Dart_PropagateError(result);
+  }
+  if (filter == NULL) {
+    Dart_ThrowException(DartEmbedder::NewInternalError("Filter destroyed"));
+  }
+  return filter;
+}
+
+static void EndFilter(Dart_Handle filter_obj, Filter* filter) {
+  Filter::SetFilterPointerNativeField(filter_obj, NULL);
+  delete filter;
+}
+
+static uint8_t* copyDictionary(Dart_Handle dictionary_obj) {
+  uint8_t* src = NULL;
+  intptr_t size;
+  Dart_TypedData_Type type;
+
+  if (Dart_IsError(Dart_ListLength(dictionary_obj, &size))) {
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Failed to get the zlib dictionary length"));
+  }
+
+  uint8_t* dictionary = new uint8_t[size];
+
+  if (dictionary == NULL) {
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Failed to allocate buffer for the zlib dictionary"));
+  }
+
+  Dart_Handle result = Dart_TypedDataAcquireData(
+      dictionary_obj, &type, reinterpret_cast<void**>(&src), &size);
+  if (!Dart_IsError(result)) {
+    memmove(dictionary, src, size);
+    Dart_TypedDataReleaseData(dictionary_obj);
+  } else {
+    if (Dart_IsError(Dart_ListGetAsBytes(dictionary_obj, 0, dictionary,
+                                         size))) {
+      Dart_ThrowException(DartEmbedder::NewInternalError(
+          "Failed to get the zlib dictionary"));
+    }
+  }
+
+  return dictionary;
+}
+
+void Filter_CreateZLibInflate(Dart_NativeArguments args) {
+  Dart_Handle filter_obj = Dart_GetNativeArgument(args, 0);
+  Dart_Handle window_bits_obj = Dart_GetNativeArgument(args, 1);
+  int64_t window_bits = DartEmbedder::GetIntegerValue(window_bits_obj);
+  Dart_Handle dict_obj = Dart_GetNativeArgument(args, 2);
+  uint8_t* dictionary = NULL;
+  intptr_t dictionary_length = 0;
+  if (!Dart_IsNull(dict_obj)) {
+    dictionary = copyDictionary(dict_obj);
+    if (dictionary != NULL) {
+      dictionary_length = 0;
+      Dart_ListLength(dict_obj, &dictionary_length);
+    }
+  }
+  Dart_Handle raw_obj = Dart_GetNativeArgument(args, 3);
+  bool raw;
+  if (Dart_IsError(Dart_BooleanValue(raw_obj, &raw))) {
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Failed to get 'raw' parameter"));
+  }
+  Filter* filter = new ZLibInflateFilter(static_cast<int32_t>(window_bits),
+                                         dictionary, dictionary_length, raw);
+  if (!filter->Init()) {
+    delete filter;
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Failed to create ZLibInflateFilter"));
+  }
+  Dart_Handle result = Filter::SetFilterPointerNativeField(filter_obj, filter);
+  if (Dart_IsError(result)) {
+    delete filter;
+    Dart_PropagateError(result);
+  }
+}
+
+void Filter_CreateZLibDeflate(Dart_NativeArguments args) {
+  Dart_Handle filter_obj = Dart_GetNativeArgument(args, 0);
+  Dart_Handle gzip_obj = Dart_GetNativeArgument(args, 1);
+  bool gzip = DartEmbedder::GetBooleanValue(gzip_obj);
+  Dart_Handle level_obj = Dart_GetNativeArgument(args, 2);
+  int64_t level = DartEmbedder::GetInt64ValueCheckRange(
+      level_obj,
+      std::numeric_limits<int32_t>::min(),
+      std::numeric_limits<int32_t>::max());
+  Dart_Handle window_bits_obj = Dart_GetNativeArgument(args, 3);
+  int64_t window_bits = DartEmbedder::GetIntegerValue(window_bits_obj);
+  Dart_Handle mLevel_obj = Dart_GetNativeArgument(args, 4);
+  int64_t mem_level = DartEmbedder::GetIntegerValue(mLevel_obj);
+  Dart_Handle strategy_obj = Dart_GetNativeArgument(args, 5);
+  int64_t strategy = DartEmbedder::GetIntegerValue(strategy_obj);
+  Dart_Handle dict_obj = Dart_GetNativeArgument(args, 6);
+  uint8_t* dictionary = NULL;
+  intptr_t dictionary_length = 0;
+  if (!Dart_IsNull(dict_obj)) {
+    dictionary = copyDictionary(dict_obj);
+    if (dictionary != NULL) {
+      dictionary_length = 0;
+      Dart_ListLength(dict_obj, &dictionary_length);
+    }
+  }
+  Dart_Handle raw_obj = Dart_GetNativeArgument(args, 7);
+  bool raw = DartEmbedder::GetBooleanValue(raw_obj);
+  Filter* filter = new ZLibDeflateFilter(gzip, static_cast<int32_t>(level),
+                                         static_cast<int32_t>(window_bits),
+                                         static_cast<int32_t>(mem_level),
+                                         static_cast<int32_t>(strategy),
+                                         dictionary, dictionary_length, raw);
+  if (!filter->Init()) {
+    delete filter;
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Failed to create ZLibDeflateFilter"));
+  }
+  Dart_Handle result = Filter::SetFilterPointerNativeField(filter_obj, filter);
+  if (Dart_IsError(result)) {
+    delete filter;
+    Dart_PropagateError(result);
+  }
+}
+
+void Filter_Process(Dart_NativeArguments args) {
+  Dart_Handle filter_obj = Dart_GetNativeArgument(args, 0);
+  Filter* filter = GetFilter(filter_obj);
+  Dart_Handle data_obj = Dart_GetNativeArgument(args, 1);
+  intptr_t start =
+      DartEmbedder::GetIntptrValue(Dart_GetNativeArgument(args, 2));
+  intptr_t end = DartEmbedder::GetIntptrValue(Dart_GetNativeArgument(args, 3));
+  intptr_t chunk_length = end - start;
+  intptr_t length;
+  Dart_TypedData_Type type;
+  uint8_t* buffer = NULL;
+  Dart_Handle result = Dart_TypedDataAcquireData(
+      data_obj, &type, reinterpret_cast<void**>(&buffer), &length);
+
+  if (!Dart_IsError(result)) {
+    DCHECK(type == Dart_TypedData_kUint8 || type == Dart_TypedData_kInt8);
+    if (type != Dart_TypedData_kUint8 && type != Dart_TypedData_kInt8) {
+      Dart_TypedDataReleaseData(data_obj);
+      Dart_ThrowException(DartEmbedder::NewInternalError(
+          "Invalid argument passed to Filter_Process"));
+    }
+    uint8_t* zlib_buffer = new uint8_t[chunk_length];
+    if (zlib_buffer == NULL) {
+      Dart_TypedDataReleaseData(data_obj);
+      Dart_ThrowException(DartEmbedder::NewInternalError(
+          "Failed to allocate buffer for zlib"));
+    }
+    memmove(zlib_buffer, buffer + start, chunk_length);
+    Dart_TypedDataReleaseData(data_obj);
+    buffer = zlib_buffer;
+  } else {
+    if (Dart_IsError(Dart_ListLength(data_obj, &length))) {
+      Dart_ThrowException(DartEmbedder::NewInternalError(
+          "Failed to get list length"));
+    }
+    buffer = new uint8_t[chunk_length];
+    if (Dart_IsError(Dart_ListGetAsBytes(
+            data_obj, start, buffer, chunk_length))) {
+      delete[] buffer;
+      Dart_ThrowException(DartEmbedder::NewInternalError(
+          "Failed to get list bytes"));
+    }
+  }
+  // Process will take ownership of buffer, if successful.
+  if (!filter->Process(buffer, chunk_length)) {
+    delete[] buffer;
+    EndFilter(filter_obj, filter);
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Call to Process while still processing data"));
+  }
+}
+
+void Filter_Processed(Dart_NativeArguments args) {
+  Dart_Handle filter_obj = Dart_GetNativeArgument(args, 0);
+  Filter* filter = GetFilter(filter_obj);
+  Dart_Handle flush_obj = Dart_GetNativeArgument(args, 1);
+  bool flush;
+  if (Dart_IsError(Dart_BooleanValue(flush_obj, &flush))) {
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Failed to get 'flush' parameter"));
+  }
+  Dart_Handle end_obj = Dart_GetNativeArgument(args, 2);
+  bool end;
+  if (Dart_IsError(Dart_BooleanValue(end_obj, &end))) {
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Failed to get 'end' parameter"));
+  }
+  intptr_t read = filter->Processed(filter->processed_buffer(),
+                                    filter->processed_buffer_size(),
+                                    flush,
+                                    end);
+  if (read < 0) {
+    // Error, end filter.
+    EndFilter(filter_obj, filter);
+    Dart_ThrowException(DartEmbedder::NewInternalError(
+        "Filter error, bad data"));
+  } else if (read == 0) {
+    Dart_SetReturnValue(args, Dart_Null());
+  } else {
+    uint8_t* io_buffer;
+    Dart_Handle result = IOBuffer::Allocate(read, &io_buffer);
+    memmove(io_buffer, filter->processed_buffer(), read);
+    Dart_SetReturnValue(args, result);
+  }
+}
+
+void Filter_End(Dart_NativeArguments args) {
+  Dart_Handle filter_obj = Dart_GetNativeArgument(args, 0);
+  Filter* filter = GetFilter(filter_obj);
+  EndFilter(filter_obj, filter);
+}
+
 void InternetAddress_Parse(Dart_NativeArguments arguments) {
   const char* address = DartEmbedder::GetStringArgument(arguments, 0);
   CHECK(address != nullptr);