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);