Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(125)

Side by Side Diff: net/filter/brotli_source_stream.cc

Issue 1662763002: [ON HOLD] Implement pull-based design for content decoding (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebased Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/filter/brotli_source_stream.h ('k') | net/filter/brotli_source_stream_disabled.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/filter/brotli_filter.h" 5 #include "net/filter/brotli_source_stream.h"
6 6
7 #include "base/bind.h"
7 #include "base/bit_cast.h" 8 #include "base/bit_cast.h"
9 #include "base/logging.h"
8 #include "base/macros.h" 10 #include "base/macros.h"
11 #include "base/memory/ptr_util.h"
9 #include "base/metrics/histogram_macros.h" 12 #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 #include "third_party/brotli/dec/decode.h"
13 14
14 namespace net { 15 namespace net {
15 16
16 namespace { 17 namespace {
18
19 const char kBrotli[] = "BROTLI";
17 const uint8_t kGzipHeader[] = {0x1f, 0x8b, 0x08}; 20 const uint8_t kGzipHeader[] = {0x1f, 0x8b, 0x08};
18 }
19 21
20 // BrotliFilter applies Brotli content decoding to a data stream. 22 // BrotliSourceStream applies Brotli content decoding to a data stream.
21 // Brotli format specification: http://www.ietf.org/id/draft-alakuijala-brotli 23 // Brotli format specification: http://www.ietf.org/id/draft-alakuijala-brotli.
22 // 24 class BrotliSourceStream : public FilterSourceStream {
23 // BrotliFilter is a subclass of Filter. See the latter's header file filter.h
24 // for sample usage.
25 class BrotliFilter : public Filter {
26 public: 25 public:
27 BrotliFilter(FilterType type) 26 explicit BrotliSourceStream(std::unique_ptr<SourceStream> upstream)
28 : Filter(type), 27 : FilterSourceStream(SourceStream::TYPE_BROTLI, std::move(upstream)),
29 decoding_status_(DecodingStatus::DECODING_IN_PROGRESS), 28 decoding_status_(DecodingStatus::DECODING_IN_PROGRESS),
30 used_memory_(0), 29 used_memory_(0),
31 used_memory_maximum_(0), 30 used_memory_maximum_(0),
32 consumed_bytes_(0), 31 consumed_bytes_(0),
33 produced_bytes_(0), 32 produced_bytes_(0),
34 gzip_header_detected_(true) { 33 gzip_header_detected_(true) {
35 brotli_state_ = BrotliCreateState(BrotliFilter::AllocateMemory, 34 brotli_state_ = BrotliCreateState(AllocateMemory, FreeMemory, this);
36 BrotliFilter::FreeMemory, this);
37 CHECK(brotli_state_); 35 CHECK(brotli_state_);
38 } 36 }
39 37
40 ~BrotliFilter() override { 38 ~BrotliSourceStream() override {
41 BrotliErrorCode error_code = BrotliGetErrorCode(brotli_state_); 39 BrotliErrorCode error_code = BrotliGetErrorCode(brotli_state_);
42 BrotliDestroyState(brotli_state_); 40 BrotliDestroyState(brotli_state_);
43 brotli_state_ = nullptr; 41 brotli_state_ = nullptr;
44 DCHECK(used_memory_ == 0); 42 DCHECK_EQ(0u, used_memory_);
45 43
46 // Don't report that gzip header was detected in case of lack of input. 44 // Don't report that gzip header was detected in case of lack of input.
47 gzip_header_detected_ &= (consumed_bytes_ >= sizeof(kGzipHeader)); 45 gzip_header_detected_ &= (consumed_bytes_ >= sizeof(kGzipHeader));
48 46
49 UMA_HISTOGRAM_ENUMERATION( 47 UMA_HISTOGRAM_ENUMERATION(
50 "BrotliFilter.Status", static_cast<int>(decoding_status_), 48 "BrotliFilter.Status", static_cast<int>(decoding_status_),
51 static_cast<int>(DecodingStatus::DECODING_STATUS_COUNT)); 49 static_cast<int>(DecodingStatus::DECODING_STATUS_COUNT));
52 UMA_HISTOGRAM_BOOLEAN("BrotliFilter.GzipHeaderDetected", 50 UMA_HISTOGRAM_BOOLEAN("BrotliFilter.GzipHeaderDetected",
53 gzip_header_detected_); 51 gzip_header_detected_);
54 if (decoding_status_ == DecodingStatus::DECODING_DONE) { 52 if (decoding_status_ == DecodingStatus::DECODING_DONE) {
55 // CompressionPercent is undefined when there is no output produced. 53 // CompressionPercent is undefined when there is no output produced.
56 if (produced_bytes_ != 0) { 54 if (produced_bytes_ != 0) {
57 UMA_HISTOGRAM_PERCENTAGE( 55 UMA_HISTOGRAM_PERCENTAGE(
58 "BrotliFilter.CompressionPercent", 56 "BrotliFilter.CompressionPercent",
59 static_cast<int>((consumed_bytes_ * 100) / produced_bytes_)); 57 static_cast<int>((consumed_bytes_ * 100) / produced_bytes_));
60 } 58 }
61 } 59 }
62 if (error_code < 0) { 60 if (error_code < 0) {
63 UMA_HISTOGRAM_ENUMERATION("BrotliFilter.ErrorCode", 61 UMA_HISTOGRAM_ENUMERATION("BrotliFilter.ErrorCode",
64 -static_cast<int>(error_code), 62 -static_cast<int>(error_code),
65 1 - BROTLI_LAST_ERROR_CODE); 63 1 - BROTLI_LAST_ERROR_CODE);
66 } 64 }
67 65
68 // All code here is for gathering stats, and can be removed when 66 // All code here is for gathering stats, and can be removed when
69 // BrotliFilter is considered stable. 67 // BrotliSourceStream is considered stable.
70 static const int kBuckets = 48; 68 const int kBuckets = 48;
71 static const int64_t kMaxKb = 1 << (kBuckets / 3); // 64MiB in KiB 69 const int64_t kMaxKb = 1 << (kBuckets / 3); // 64MiB in KiB
72 UMA_HISTOGRAM_CUSTOM_COUNTS("BrotliFilter.UsedMemoryKB", 70 UMA_HISTOGRAM_CUSTOM_COUNTS("BrotliFilter.UsedMemoryKB",
73 used_memory_maximum_ / 1024, 1, kMaxKb, 71 used_memory_maximum_ / 1024, 1, kMaxKb,
74 kBuckets); 72 kBuckets);
75 } 73 }
76 74
77 // Decodes the pre-filter data and writes the output into the |dest_buffer| 75 private:
78 // passed in. 76 // Reported in UMA and must be kept in sync with the histograms.xml file.
79 // The function returns FilterStatus. See filter.h for its description. 77 enum class DecodingStatus : int {
80 // 78 DECODING_IN_PROGRESS = 0,
81 // Upon entry, |*dest_len| is the total size (in number of chars) of the 79 DECODING_DONE,
82 // destination buffer. Upon exit, |*dest_len| is the actual number of chars 80 DECODING_ERROR,
83 // written into the destination buffer.
84 //
85 // This function will fail if there is no pre-filter data in the
86 // |stream_buffer_|. On the other hand, |*dest_len| can be 0 upon successful
87 // return. For example, decompressor may process some pre-filter data
88 // but not produce output yet.
89 FilterStatus ReadFilteredData(char* dest_buffer, int* dest_len) override {
90 if (!dest_buffer || !dest_len)
91 return Filter::FILTER_ERROR;
92 81
93 if (decoding_status_ == DecodingStatus::DECODING_DONE) { 82 DECODING_STATUS_COUNT
94 *dest_len = 0; 83 // DECODING_STATUS_COUNT must always be the last element in this enum.
95 return Filter::FILTER_DONE; 84 };
96 }
97 85
98 if (decoding_status_ != DecodingStatus::DECODING_IN_PROGRESS) 86 // SourceStream implementation
99 return Filter::FILTER_ERROR; 87 std::string GetTypeAsString() const override { return kBrotli; }
100 88
101 size_t output_buffer_size = base::checked_cast<size_t>(*dest_len); 89 int FilterData(IOBuffer* output_buffer,
102 size_t input_buffer_size = base::checked_cast<size_t>(stream_data_len_); 90 size_t output_buffer_size,
103 91 DrainableIOBuffer* input_buffer) override {
104 size_t available_in = input_buffer_size; 92 const uint8_t* next_in = bit_cast<uint8_t*>(input_buffer->data());
105 const uint8_t* next_in = bit_cast<uint8_t*>(next_stream_data_); 93 size_t available_in = input_buffer->BytesRemaining();
94 uint8_t* next_out = bit_cast<uint8_t*>(output_buffer->data());
106 size_t available_out = output_buffer_size; 95 size_t available_out = output_buffer_size;
107 uint8_t* next_out = bit_cast<uint8_t*>(dest_buffer);
108 size_t total_out = 0; 96 size_t total_out = 0;
109
110 // Check if start of the input stream looks like gzip stream. 97 // Check if start of the input stream looks like gzip stream.
111 for (size_t i = consumed_bytes_; i < sizeof(kGzipHeader); ++i) { 98 for (size_t i = consumed_bytes_; i < sizeof(kGzipHeader); ++i) {
112 if (!gzip_header_detected_) 99 if (!gzip_header_detected_)
113 break; 100 break;
114 size_t j = i - consumed_bytes_; 101 size_t j = i - consumed_bytes_;
115 if (j < available_in && kGzipHeader[i] != next_in[j]) 102 if (j < available_in && kGzipHeader[i] != next_in[j])
116 gzip_header_detected_ = false; 103 gzip_header_detected_ = false;
117 } 104 }
118 105
119 BrotliResult result = 106 BrotliResult result =
120 BrotliDecompressStream(&available_in, &next_in, &available_out, 107 BrotliDecompressStream(&available_in, &next_in, &available_out,
121 &next_out, &total_out, brotli_state_); 108 &next_out, &total_out, brotli_state_);
122 109
123 CHECK(available_in <= input_buffer_size); 110 size_t bytes_used = input_buffer->BytesRemaining() - available_in;
124 CHECK(available_out <= output_buffer_size); 111 size_t bytes_written = output_buffer_size - available_out;
125 consumed_bytes_ += input_buffer_size - available_in; 112 CHECK_GE(bytes_used, 0u);
126 produced_bytes_ += output_buffer_size - available_out; 113 CHECK_GE(bytes_written, 0u);
114 produced_bytes_ += bytes_written;
115 consumed_bytes_ += bytes_used;
127 116
128 base::CheckedNumeric<size_t> safe_bytes_written(output_buffer_size); 117 input_buffer->DidConsume(bytes_used);
129 safe_bytes_written -= available_out;
130 int bytes_written =
131 base::checked_cast<int>(safe_bytes_written.ValueOrDie());
132 118
133 switch (result) { 119 switch (result) {
134 case BROTLI_RESULT_NEEDS_MORE_OUTPUT: 120 case BROTLI_RESULT_NEEDS_MORE_OUTPUT:
135 // Fall through. 121 return bytes_written;
136 case BROTLI_RESULT_SUCCESS: 122 case BROTLI_RESULT_SUCCESS:
137 *dest_len = bytes_written; 123 decoding_status_ = DecodingStatus::DECODING_DONE;
138 stream_data_len_ = base::checked_cast<int>(available_in); 124 return bytes_written;
139 next_stream_data_ = bit_cast<char*>(next_in);
140 if (result == BROTLI_RESULT_SUCCESS) {
141 decoding_status_ = DecodingStatus::DECODING_DONE;
142 return Filter::FILTER_DONE;
143 }
144 return Filter::FILTER_OK;
145
146 case BROTLI_RESULT_NEEDS_MORE_INPUT: 125 case BROTLI_RESULT_NEEDS_MORE_INPUT:
147 *dest_len = bytes_written; 126 // Decompress needs more input has consumed all existing input.
148 stream_data_len_ = 0; 127 DCHECK_EQ(0, input_buffer->BytesRemaining());
149 next_stream_data_ = nullptr; 128 decoding_status_ = DecodingStatus::DECODING_IN_PROGRESS;
150 return Filter::FILTER_NEED_MORE_DATA; 129 return bytes_written;
151 130 // If the decompressor threw an error, fail synchronously.
152 default: 131 default:
153 decoding_status_ = DecodingStatus::DECODING_ERROR; 132 decoding_status_ = DecodingStatus::DECODING_ERROR;
154 return Filter::FILTER_ERROR; 133 return ERR_CONTENT_DECODING_FAILED;
155 } 134 }
156 } 135 }
157 136
158 private:
159 static void* AllocateMemory(void* opaque, size_t size) { 137 static void* AllocateMemory(void* opaque, size_t size) {
160 BrotliFilter* filter = reinterpret_cast<BrotliFilter*>(opaque); 138 BrotliSourceStream* filter = reinterpret_cast<BrotliSourceStream*>(opaque);
161 return filter->AllocateMemoryInternal(size); 139 return filter->AllocateMemoryInternal(size);
162 } 140 }
163 141
164 static void FreeMemory(void* opaque, void* address) { 142 static void FreeMemory(void* opaque, void* address) {
165 BrotliFilter* filter = reinterpret_cast<BrotliFilter*>(opaque); 143 BrotliSourceStream* filter = reinterpret_cast<BrotliSourceStream*>(opaque);
166 filter->FreeMemoryInternal(address); 144 filter->FreeMemoryInternal(address);
167 } 145 }
168 146
169 void* AllocateMemoryInternal(size_t size) { 147 void* AllocateMemoryInternal(size_t size) {
170 size_t* array = reinterpret_cast<size_t*>(malloc(size + sizeof(size_t))); 148 size_t* array = reinterpret_cast<size_t*>(malloc(size + sizeof(size_t)));
171 if (!array) 149 if (!array)
172 return nullptr; 150 return nullptr;
173 used_memory_ += size; 151 used_memory_ += size;
174 if (used_memory_maximum_ < used_memory_) 152 if (used_memory_maximum_ < used_memory_)
175 used_memory_maximum_ = used_memory_; 153 used_memory_maximum_ = used_memory_;
176 array[0] = size; 154 array[0] = size;
177 return &array[1]; 155 return &array[1];
178 } 156 }
179 157
180 void FreeMemoryInternal(void* address) { 158 void FreeMemoryInternal(void* address) {
181 if (!address) 159 if (!address)
182 return; 160 return;
183 size_t* array = reinterpret_cast<size_t*>(address); 161 size_t* array = reinterpret_cast<size_t*>(address);
184 used_memory_ -= array[-1]; 162 used_memory_ -= array[-1];
185 free(&array[-1]); 163 free(&array[-1]);
186 } 164 }
187 165
188 // Reported in UMA and must be kept in sync with the histograms.xml file. 166 BrotliState* brotli_state_;
189 enum class DecodingStatus : int {
190 DECODING_IN_PROGRESS = 0,
191 DECODING_DONE,
192 DECODING_ERROR,
193 167
194 DECODING_STATUS_COUNT
195 // DECODING_STATUS_COUNT must always be the last element in this enum.
196 };
197
198 // Tracks the status of decoding.
199 // This variable is updated only by ReadFilteredData.
200 DecodingStatus decoding_status_; 168 DecodingStatus decoding_status_;
201 169
202 BrotliState* brotli_state_;
203
204 size_t used_memory_; 170 size_t used_memory_;
205 size_t used_memory_maximum_; 171 size_t used_memory_maximum_;
206 size_t consumed_bytes_; 172 size_t consumed_bytes_;
207 size_t produced_bytes_; 173 size_t produced_bytes_;
208 174
209 bool gzip_header_detected_; 175 bool gzip_header_detected_;
210 176
211 DISALLOW_COPY_AND_ASSIGN(BrotliFilter); 177 DISALLOW_COPY_AND_ASSIGN(BrotliSourceStream);
212 }; 178 };
213 179
214 Filter* CreateBrotliFilter(Filter::FilterType type_id) { 180 } // namespace
215 return new BrotliFilter(type_id); 181
182 std::unique_ptr<FilterSourceStream> CreateBrotliSourceStream(
183 std::unique_ptr<SourceStream> previous) {
184 return base::WrapUnique(new BrotliSourceStream(std::move(previous)));
216 } 185 }
217 186
218 } // namespace net 187 } // namespace net
OLDNEW
« no previous file with comments | « net/filter/brotli_source_stream.h ('k') | net/filter/brotli_source_stream_disabled.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698