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

Side by Side Diff: net/filter/brotli_filter.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: Address comments Created 4 years, 8 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
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698