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

Side by Side Diff: net/filter/sdch_stream_source.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: Created 4 years, 10 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 2016 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 "base/auto_reset.h"
6 #include "base/bind.h"
7 #include "net/filter/block_buffer.h"
8 #include "net/filter/sdch_stream_source.h"
9 #include "sdch/open-vcdiff/src/google/vcdecoder.h"
10
11 namespace {
12
13 const size_t kServerIdLength = 9;
14
15 } // namespace
16
17 namespace net {
18
19 SdchStreamSource::SdchStreamSource(scoped_ptr<StreamSource> previous,
20 SdchStreamSourceDelegate* delegate)
21 : buffer_(new BlockBuffer),
22 previous_(std::move(previous)),
23 decoder_(new open_vcdiff::VCDiffStreamingDecoder),
24 delegate_(delegate),
25 output_replaced_(false),
26 passthrough_(false),
27 dictionary_tried_load_(false),
28 total_bytes_output_(0) {}
29
30 SdchStreamSource::~SdchStreamSource() {
31 // In unit tests, delegate_ may be null.
32 if (delegate_)
33 delegate_->NotifyStreamDone();
34 }
35
36 bool SdchStreamSource::Init() {
37 return true;
38 }
39
40 void SdchStreamSource::StopDecoding() {
41 DCHECK(in_delegate_handler_);
42 passthrough_ = true;
43 }
44
45 void SdchStreamSource::ReplaceOutput(const char* data, size_t size) {
46 DCHECK(in_delegate_handler_);
47 buffered_output_.assign(data, size);
48 }
49
50 net::Error SdchStreamSource::Read(
51 IOBuffer* dest_buffer,
52 size_t buffer_size,
53 size_t* bytes_read,
54 const StreamSource::OnReadCompleteCallback& callback) {
55 *bytes_read = 0;
56
57 Error error;
58 do {
59 // If there is no loaded dictionary, try loading a dictionary ID from
60 // |buffer_| and then loading a dictionary. If anything goes wrong in
61 // LoadDictionary, the delegate's HandleDictionaryError() call is expected
62 // to recover, either by calling StopDecoding() or ReplaceOutput(). Both of
63 // these are handled by Decompress (as is having no dictionary loaded). If
64 // the delegate's HandleDictionaryError cannot recover, this function
65 // returns a read error.
66 if (!dictionary_tried_load_ && !LoadDictionary())
67 return ERR_FAILED;
68
69 // Run the synchronous decompressor.
70 SdchStreamState state = Decompress(dest_buffer, buffer_size, bytes_read);
71
72 // If decompression failed, call HandleDecodingError on the delegate, then
73 // rerun the decompressor. The decompressor shouldn't actually decompress
74 // this time, since the delegate should have called either Passthrough or
75 // ReplaceOutput.
76 if (state == SDCH_STREAM_ERROR) {
77 bool handled = AskDelegateToHandleDecodingError();
78 if (!handled)
79 return ERR_FAILED;
80 state = Decompress(dest_buffer, buffer_size, bytes_read);
81 }
82
83 if (*bytes_read > 0) {
84 total_bytes_output_ += *bytes_read;
85 return OK;
86 }
87
88 DCHECK(state == SDCH_STREAM_MORE_INPUT);
89 DCHECK(!buffer_->HasMoreBytes());
90
91 // If someone called ReplaceOutput to replace this stream's output,
92 // Decompress will return all the buffered output; if it did not do so, this
93 // stream is at EOF.
94 if (output_replaced_)
95 return OK;
96
97 size_t previous_bytes_read;
98 error = previous_->Read(
99 buffer_->buffer(), buffer_->size(), &previous_bytes_read,
100 base::Bind(&SdchStreamSource::OnReadComplete, base::Unretained(this),
101 callback, base::Unretained(dest_buffer), buffer_size));
102
103 // Synchronous EOF from previous source.
104 if (error == OK && previous_bytes_read == 0)
105 return OK;
106
107 // Synchronous success from previous source.
108 if (error == OK)
109 buffer_->WasRefilled(previous_bytes_read);
110 } while (error == net::OK);
111
112 return error;
113 }
114
115 void SdchStreamSource::OnReadComplete(
116 const StreamSource::OnReadCompleteCallback& callback,
117 IOBuffer* dest_buffer,
118 size_t dest_buffer_size,
119 Error error,
120 size_t bytes_read) {
121 DCHECK(!buffer_->HasMoreBytes());
122
123 // Previous source Read failed. Fail this read too.
124 if (error != OK) {
125 callback.Run(error, bytes_read);
126 return;
127 }
128
129 if (bytes_read == 0) {
130 callback.Run(OK, 0);
131 }
132
133 buffer_->WasRefilled(bytes_read);
134 error = Read(dest_buffer, dest_buffer_size, &bytes_read, callback);
135 if (error != ERR_IO_PENDING) {
136 if (error == OK)
137 total_bytes_output_ += bytes_read;
138 callback.Run(error, bytes_read);
139 }
140 }
141
142 // Synchronous decompressor core.
143 SdchStreamSource::SdchStreamState SdchStreamSource::Decompress(
144 IOBuffer* dest_buffer,
145 size_t buffer_size,
146 size_t* bytes_read) {
147 // If output has been replaced via ReplaceOutput, short-circuit any other
148 // processing, and emit as much of the buffered output as possible. If there's
149 // no buffered output left, return EOF.
150 if (output_replaced_)
151 return Flush(dest_buffer, buffer_size, bytes_read);
152
153 // If this stream is no longer decoding, pass any input through.
154 if (passthrough_)
155 return Passthrough(dest_buffer, buffer_size, bytes_read);
156
157 // Can't do any decoding until a dictionary is loaded, which requires more
158 // input.
159 if (!dictionary_tried_load_)
160 return SDCH_STREAM_MORE_INPUT;
161
162 // If there's any data left in the output buffer, try flushing some of it.
163 if (Flush(dest_buffer, buffer_size, bytes_read) != SDCH_STREAM_MORE_INPUT)
164 return SDCH_STREAM_MORE_OUTPUT_SPACE;
165
166 // This is awkward. In principle, this function is called only when the input
167 // buffer has more bytes, since vcdiff always consumes input; unfortunately,
168 // the dictionary-loading code earlier in the Read() loop can consume all the
169 // currently buffered input. If that happened, Decompress returns early here.
170 if (!buffer_->HasMoreBytes())
171 return SDCH_STREAM_MORE_INPUT;
172
173 // Now there is no data left in the output buffer and there is data in the
174 // input buffer, so run the vcdiff decoder.
175 DCHECK(buffered_output_.empty());
176 DCHECK(buffer_->HasMoreBytes());
177
178 // Calls to DecodeChunk always consume all their input, so this always drains
179 // the entire buffer.
180 bool ok = decoder_->DecodeChunk(buffer_->bytes(), buffer_->bytes_left(),
181 &buffered_output_);
182 if (ok) {
183 buffer_->WasDrained(buffer_->bytes_left());
184 } else {
185 if (!AskDelegateToHandleDecodingError())
186 return SDCH_STREAM_ERROR;
187 }
188
189 return Flush(dest_buffer, buffer_size, bytes_read);
190 }
191
192 SdchStreamSource::SdchStreamState SdchStreamSource::Passthrough(
193 IOBuffer* dest_buffer,
194 size_t buffer_size,
195 size_t* bytes_read) {
196 SdchStreamState state = Flush(dest_buffer, buffer_size, bytes_read);
197 if (state == SDCH_STREAM_MORE_OUTPUT_SPACE)
198 return state;
199
200 // No buffered data to flush. See if there's any input.
201 DCHECK(buffered_output_.empty());
202 if (!buffer_->HasMoreBytes())
203 return SDCH_STREAM_MORE_INPUT;
204
205 size_t to_copy = buffer_size;
206 if (to_copy > buffer_->bytes_left())
207 to_copy = buffer_->bytes_left();
208 memcpy(dest_buffer->data(), buffer_->bytes(), to_copy);
209 buffer_->WasDrained(to_copy);
210 if (buffer_->HasMoreBytes())
211 return SDCH_STREAM_MORE_OUTPUT_SPACE;
212 else
213 return SDCH_STREAM_MORE_INPUT;
214 }
215
216 SdchStreamSource::SdchStreamState SdchStreamSource::Flush(IOBuffer* dest_buffer,
217 size_t buffer_size,
218 size_t* bytes_read) {
219 size_t to_copy = buffer_size;
220 if (to_copy > buffered_output_.length())
221 to_copy = buffered_output_.length();
222 if (to_copy == 0)
223 return SDCH_STREAM_MORE_INPUT;
224 memcpy(dest_buffer->data(), buffered_output_.data(), to_copy);
225 buffered_output_.erase(0, to_copy);
226 *bytes_read = to_copy;
227 return SDCH_STREAM_MORE_OUTPUT_SPACE;
228 }
229
230 // Attempts to find a dictionary ID at the start of the input stream and load
231 // the dictionary with that identifier. Returns true if no error happened or an
232 // error happened but the delegate repaired it, and false if an error happened
233 // and the delegate did not repair it.
234 bool SdchStreamSource::LoadDictionary() {
235 size_t to_copy = kServerIdLength - dictionary_id_.length();
236 const std::string* dictionary_text = nullptr;
237 if (to_copy > buffer_->bytes_left())
238 to_copy = buffer_->bytes_left();
239 dictionary_id_.append(buffer_->bytes(), to_copy);
240 buffer_->WasDrained(to_copy);
241
242 // Not enough bytes for a dictionary ID accumulated yet.
243 if (dictionary_id_.length() != kServerIdLength)
244 return true;
245
246 dictionary_tried_load_ = true;
247
248 // If the dictionary ID looks bogus, or the delegate can't find a dictionary
249 // with that identifier, then call HandleDictionaryError and bail.
250 // Also, stick the dictionary ID into the output buffer here; LoadDictionary
251 // will consume it assuming it's a dictionary ID, so if it's really not, the
252 // "dictionary ID" should appear in the output stream.
253 //
254 // To avoid passing a std::string with a null terminator into GetDictionary(),
255 // |stem_id| here removes the last byte blindly, and this method only calls
256 // GetDictionary with it if CouldBeDictionaryId returns true.
257 std::string stem_id = dictionary_id_.substr(0, kServerIdLength - 1);
258 if (!CouldBeDictionaryId(dictionary_id_) ||
259 !delegate_->GetDictionary(stem_id, &dictionary_text)) {
260 buffered_output_.append(dictionary_id_);
261 return AskDelegateToHandleDictionaryError();
262 }
263
264 decoder_->StartDecoding(dictionary_text->data(), dictionary_text->length());
265 return true;
266 }
267
268 bool SdchStreamSource::CouldBeDictionaryId(const std::string& id) const {
269 std::string kBase64UrlChars =
270 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
271 for (size_t i = 0; i < kServerIdLength - 1; i++) {
272 if (kBase64UrlChars.find(id[i]) == std::string::npos)
273 return false;
274 }
275 if (id[kServerIdLength - 1] != '\0')
276 return false;
277 return true;
278 }
279
280 size_t SdchStreamSource::GetBytesOutput() const {
281 return total_bytes_output_;
282 }
283
284 bool SdchStreamSource::AskDelegateToHandleDictionaryError() {
285 base::AutoReset<bool> resetter(&in_delegate_handler_, true);
286 return delegate_->HandleDictionaryError(this);
287 }
288
289 bool SdchStreamSource::AskDelegateToHandleDecodingError() {
290 base::AutoReset<bool> resetter(&in_delegate_handler_, true);
291 return delegate_->HandleDecodingError(this);
292 }
293
294 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698