// 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 "blimp/net/compressed_packet_writer.h" #include #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/sys_byteorder.h" #include "blimp/net/common.h" #include "net/base/io_buffer.h" #include "third_party/zlib/zlib.h" namespace blimp { namespace { // Allocate the maxmimum amount of memory to deflate (512KB) for higher // compression. (See zconf.h for details on memLevel semantics.) const int kZlibMemoryLevel = 9; } // namespace CompressedPacketWriter::CompressedPacketWriter( std::unique_ptr sink) : sink_(std::move(sink)), compressed_buf_(new net::GrowableIOBuffer) { DCHECK(sink_); memset(&zlib_stream_, 0, sizeof(z_stream)); // MAX_WBITS means we are using the maximal window size for decompression; // negating it means that we are ignoring headers and CRC checks. int init_error = deflateInit2(&zlib_stream_, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, // Negative value = no headers or CRC. kZlibMemoryLevel, Z_DEFAULT_STRATEGY); CHECK_EQ(Z_OK, init_error); } CompressedPacketWriter::~CompressedPacketWriter() { deflateEnd(&zlib_stream_); } void CompressedPacketWriter::WritePacket( const scoped_refptr& write_buffer, const net::CompletionCallback& callback) { DCHECK(write_buffer); DCHECK(!callback.is_null()); size_t uncompressed_size = base::checked_cast(write_buffer->BytesRemaining()); // Zero-length input => zero-length output. if (uncompressed_size == 0) { sink_->WritePacket(write_buffer, callback); return; } // Don't compress anything that's bigger than the maximum allowable payload // size. if (uncompressed_size > kMaxPacketPayloadSizeBytes) { callback.Run(net::ERR_FILE_TOO_BIG); return; } int compress_result = Compress(write_buffer, compressed_buf_); if (compress_result < 0) { callback.Run(compress_result); return; } #if !defined(NDEBUG) uncompressed_size_total_ += uncompressed_size; compressed_size_total_ += compress_result; #endif // !defined(NDEBUG) scoped_refptr compressed_outbuf( new net::DrainableIOBuffer(compressed_buf_.get(), compress_result)); sink_->WritePacket(compressed_outbuf, callback); DVLOG(4) << "deflate packet: " << uncompressed_size << " in, " << compress_result << " out."; DVLOG(3) << "deflate total: " << uncompressed_size_total_ << " in, " << compressed_size_total_ << " out."; } int CompressedPacketWriter::Compress( const scoped_refptr& src_buf, const scoped_refptr& dest_buf) { DCHECK_EQ(0, dest_buf->offset()); const int zlib_output_ubound = deflateBound(&zlib_stream_, src_buf->BytesRemaining()); if (dest_buf->capacity() < zlib_output_ubound) { dest_buf->SetCapacity(zlib_output_ubound); } zlib_stream_.next_in = reinterpret_cast(src_buf->data()); zlib_stream_.avail_in = static_cast(src_buf->BytesRemaining()); zlib_stream_.next_out = reinterpret_cast(dest_buf->data()); zlib_stream_.avail_out = static_cast(zlib_output_ubound); int deflate_error = deflate(&zlib_stream_, Z_SYNC_FLUSH); if (deflate_error != Z_OK) { DLOG(FATAL) << "Unexpected deflate() return value: " << deflate_error; return net::ERR_UNEXPECTED; } if (zlib_stream_.avail_in > 0) { DLOG(ERROR) << "deflate() did not consume all data, remainder: " << zlib_stream_.avail_in << " bytes."; return net::ERR_UNEXPECTED; } return zlib_output_ubound - zlib_stream_.avail_out; } } // namespace blimp