| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/filter/brotli_filter.h" | |
| 6 | |
| 7 #include "base/bit_cast.h" | |
| 8 #include "base/macros.h" | |
| 9 #include "base/metrics/histogram_macros.h" | |
| 10 #include "base/numerics/safe_conversions.h" | |
| 11 #include "base/numerics/safe_math.h" | |
| 12 #include "third_party/brotli/dec/decode.h" | |
| 13 | |
| 14 namespace net { | |
| 15 | |
| 16 // BrotliFilter applies Brotli content decoding to a data stream. | |
| 17 // Brotli format specification: http://www.ietf.org/id/draft-alakuijala-brotli | |
| 18 // | |
| 19 // BrotliFilter is a subclass of Filter. See the latter's header file filter.h | |
| 20 // for sample usage. | |
| 21 class BrotliFilter : public Filter { | |
| 22 public: | |
| 23 BrotliFilter(FilterType type) | |
| 24 : Filter(type), | |
| 25 decoding_status_(DecodingStatus::DECODING_IN_PROGRESS), | |
| 26 used_memory_(0), | |
| 27 used_memory_maximum_(0), | |
| 28 consumed_bytes_(0), | |
| 29 produced_bytes_(0) { | |
| 30 brotli_state_ = BrotliCreateState(BrotliFilter::AllocateMemory, | |
| 31 BrotliFilter::FreeMemory, this); | |
| 32 CHECK(brotli_state_); | |
| 33 } | |
| 34 | |
| 35 ~BrotliFilter() override { | |
| 36 BrotliDestroyState(brotli_state_); | |
| 37 brotli_state_ = nullptr; | |
| 38 DCHECK(used_memory_ == 0); | |
| 39 | |
| 40 UMA_HISTOGRAM_ENUMERATION( | |
| 41 "BrotliFilter.Status", static_cast<int>(decoding_status_), | |
| 42 static_cast<int>(DecodingStatus::DECODING_STATUS_COUNT)); | |
| 43 if (decoding_status_ == DecodingStatus::DECODING_DONE) { | |
| 44 // CompressionPercent is undefined when there is no output produced. | |
| 45 if (produced_bytes_ != 0) { | |
| 46 UMA_HISTOGRAM_PERCENTAGE( | |
| 47 "BrotliFilter.CompressionPercent", | |
| 48 static_cast<int>((consumed_bytes_ * 100) / produced_bytes_)); | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 // All code here is for gathering stats, and can be removed when | |
| 53 // BrotliFilter is considered stable. | |
| 54 static const int kBuckets = 48; | |
| 55 static const int64_t kMaxKb = 1 << (kBuckets / 3); // 64MiB in KiB | |
| 56 UMA_HISTOGRAM_CUSTOM_COUNTS("BrotliFilter.UsedMemoryKB", | |
| 57 used_memory_maximum_ / 1024, 1, kMaxKb, | |
| 58 kBuckets); | |
| 59 } | |
| 60 | |
| 61 // Decodes the pre-filter data and writes the output into the |dest_buffer| | |
| 62 // passed in. | |
| 63 // The function returns FilterStatus. See filter.h for its description. | |
| 64 // | |
| 65 // Upon entry, |*dest_len| is the total size (in number of chars) of the | |
| 66 // destination buffer. Upon exit, |*dest_len| is the actual number of chars | |
| 67 // written into the destination buffer. | |
| 68 // | |
| 69 // This function will fail if there is no pre-filter data in the | |
| 70 // |stream_buffer_|. On the other hand, |*dest_len| can be 0 upon successful | |
| 71 // return. For example, decompressor may process some pre-filter data | |
| 72 // but not produce output yet. | |
| 73 FilterStatus ReadFilteredData(char* dest_buffer, int* dest_len) override { | |
| 74 if (!dest_buffer || !dest_len) | |
| 75 return Filter::FILTER_ERROR; | |
| 76 | |
| 77 if (decoding_status_ == DecodingStatus::DECODING_DONE) { | |
| 78 *dest_len = 0; | |
| 79 return Filter::FILTER_DONE; | |
| 80 } | |
| 81 | |
| 82 if (decoding_status_ != DecodingStatus::DECODING_IN_PROGRESS) | |
| 83 return Filter::FILTER_ERROR; | |
| 84 | |
| 85 size_t output_buffer_size = base::checked_cast<size_t>(*dest_len); | |
| 86 size_t input_buffer_size = base::checked_cast<size_t>(stream_data_len_); | |
| 87 | |
| 88 size_t available_in = input_buffer_size; | |
| 89 const uint8_t* next_in = bit_cast<uint8_t*>(next_stream_data_); | |
| 90 size_t available_out = output_buffer_size; | |
| 91 uint8_t* next_out = bit_cast<uint8_t*>(dest_buffer); | |
| 92 size_t total_out = 0; | |
| 93 BrotliResult result = | |
| 94 BrotliDecompressStream(&available_in, &next_in, &available_out, | |
| 95 &next_out, &total_out, brotli_state_); | |
| 96 | |
| 97 CHECK(available_in <= input_buffer_size); | |
| 98 CHECK(available_out <= output_buffer_size); | |
| 99 consumed_bytes_ += input_buffer_size - available_in; | |
| 100 produced_bytes_ += output_buffer_size - available_out; | |
| 101 | |
| 102 base::CheckedNumeric<size_t> safe_bytes_written(output_buffer_size); | |
| 103 safe_bytes_written -= available_out; | |
| 104 int bytes_written = | |
| 105 base::checked_cast<int>(safe_bytes_written.ValueOrDie()); | |
| 106 | |
| 107 switch (result) { | |
| 108 case BROTLI_RESULT_NEEDS_MORE_OUTPUT: | |
| 109 // Fall through. | |
| 110 case BROTLI_RESULT_SUCCESS: | |
| 111 *dest_len = bytes_written; | |
| 112 stream_data_len_ = base::checked_cast<int>(available_in); | |
| 113 next_stream_data_ = bit_cast<char*>(next_in); | |
| 114 if (result == BROTLI_RESULT_SUCCESS) { | |
| 115 decoding_status_ = DecodingStatus::DECODING_DONE; | |
| 116 return Filter::FILTER_DONE; | |
| 117 } | |
| 118 return Filter::FILTER_OK; | |
| 119 | |
| 120 case BROTLI_RESULT_NEEDS_MORE_INPUT: | |
| 121 *dest_len = bytes_written; | |
| 122 stream_data_len_ = 0; | |
| 123 next_stream_data_ = nullptr; | |
| 124 return Filter::FILTER_NEED_MORE_DATA; | |
| 125 | |
| 126 default: | |
| 127 decoding_status_ = DecodingStatus::DECODING_ERROR; | |
| 128 return Filter::FILTER_ERROR; | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 private: | |
| 133 static void* AllocateMemory(void* opaque, size_t size) { | |
| 134 BrotliFilter* filter = reinterpret_cast<BrotliFilter*>(opaque); | |
| 135 return filter->AllocateMemoryInternal(size); | |
| 136 } | |
| 137 | |
| 138 static void FreeMemory(void* opaque, void* address) { | |
| 139 BrotliFilter* filter = reinterpret_cast<BrotliFilter*>(opaque); | |
| 140 filter->FreeMemoryInternal(address); | |
| 141 } | |
| 142 | |
| 143 void* AllocateMemoryInternal(size_t size) { | |
| 144 size_t* array = reinterpret_cast<size_t*>(malloc(size + sizeof(size_t))); | |
| 145 if (!array) | |
| 146 return nullptr; | |
| 147 used_memory_ += size; | |
| 148 if (used_memory_maximum_ < used_memory_) | |
| 149 used_memory_maximum_ = used_memory_; | |
| 150 array[0] = size; | |
| 151 return &array[1]; | |
| 152 } | |
| 153 | |
| 154 void FreeMemoryInternal(void* address) { | |
| 155 if (!address) | |
| 156 return; | |
| 157 size_t* array = reinterpret_cast<size_t*>(address); | |
| 158 used_memory_ -= array[-1]; | |
| 159 free(&array[-1]); | |
| 160 } | |
| 161 | |
| 162 // Reported in UMA and must be kept in sync with the histograms.xml file. | |
| 163 enum class DecodingStatus : int { | |
| 164 DECODING_IN_PROGRESS = 0, | |
| 165 DECODING_DONE, | |
| 166 DECODING_ERROR, | |
| 167 | |
| 168 DECODING_STATUS_COUNT | |
| 169 // DECODING_STATUS_COUNT must always be the last element in this enum. | |
| 170 }; | |
| 171 | |
| 172 // Tracks the status of decoding. | |
| 173 // This variable is updated only by ReadFilteredData. | |
| 174 DecodingStatus decoding_status_; | |
| 175 | |
| 176 BrotliState* brotli_state_; | |
| 177 | |
| 178 size_t used_memory_; | |
| 179 size_t used_memory_maximum_; | |
| 180 size_t consumed_bytes_; | |
| 181 size_t produced_bytes_; | |
| 182 | |
| 183 DISALLOW_COPY_AND_ASSIGN(BrotliFilter); | |
| 184 }; | |
| 185 | |
| 186 Filter* CreateBrotliFilter(Filter::FilterType type_id) { | |
| 187 return new BrotliFilter(type_id); | |
| 188 } | |
| 189 | |
| 190 } // namespace net | |
| OLD | NEW |