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

Side by Side Diff: net/filter/sdch_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/sdch_source_stream.h ('k') | net/filter/sdch_source_stream_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "net/filter/sdch_source_stream.h"
6
7 #include "base/auto_reset.h"
8 #include "base/bind.h"
9 #include "base/numerics/safe_conversions.h"
10 #include "sdch/open-vcdiff/src/google/vcdecoder.h"
11
12 namespace net {
13
14 namespace {
15
16 const size_t kServerIdLength = 9;
17 const char kSDCH[] = "SDCH";
18
19 } // namespace
20
21 SdchSourceStream::SdchSourceStream(std::unique_ptr<SourceStream> previous,
22 Delegate* delegate)
23 : FilterSourceStream(SourceStream::TYPE_SDCH, std::move(previous)),
24 delegate_(delegate),
25 output_replaced_(false),
26 passthrough_(false),
27 dictionary_tried_load_(false) {
28 CHECK(delegate);
29 }
30
31 SdchSourceStream::~SdchSourceStream() {}
32
33 void SdchSourceStream::StopDecoding() {
34 DCHECK(in_delegate_handler_);
35 passthrough_ = true;
36 }
37
38 void SdchSourceStream::ReplaceOutput(const char* data, size_t size) {
39 DCHECK(in_delegate_handler_);
40 output_replaced_ = true;
41 buffered_output_.assign(data, size);
42 }
43
44 std::string SdchSourceStream::GetTypeAsString() const {
45 return kSDCH;
46 }
47
48 int SdchSourceStream::FilterData(IOBuffer* output_buffer,
49 size_t output_buffer_size,
50 DrainableIOBuffer* input_buffer) {
51 size_t bytes_read = 0;
52 // Run the synchronous decompressor.
53 SdchStreamState state =
54 Decompress(output_buffer, output_buffer_size, input_buffer, &bytes_read);
55
56 // If decompression failed, call OnDecodingError on the delegate, then
57 // rerun the decompressor. The decompressor shouldn't actually decompress
58 // this time, since the delegate should have called either PassThrough or
59 // ReplaceOutput.
60 if (state == SDCH_STREAM_ERROR) {
61 input_buffer->DidConsume(input_buffer->BytesRemaining());
62 bool handled = AskDelegateToHandleDecodingError();
63 if (!handled)
64 return ERR_FAILED;
65 state = Decompress(output_buffer, output_buffer_size, input_buffer,
66 &bytes_read);
67 }
68
69 DCHECK_EQ(0, input_buffer->BytesRemaining());
70 return bytes_read;
71 }
72
73 // Synchronous decompressor core.
74 SdchSourceStream::SdchStreamState SdchSourceStream::Decompress(
75 IOBuffer* output_buffer,
76 size_t output_buffer_size,
77 DrainableIOBuffer* input_buffer,
78 size_t* bytes_read) {
79 // If there is no loaded dictionary, try loading a dictionary ID from
80 // |input_buffer| and then loading a dictionary. If anything goes wrong in
81 // LoadDictionary, the delegate's OnDictionaryError() call is expected
82 // to recover, either by calling StopDecoding() or ReplaceOutput(). Both of
83 // these are handled by Decompress (as is having no dictionary loaded). If
84 // the delegate's OnDictionaryError cannot recover, this function
85 // returns a read error.
86 if (!dictionary_tried_load_ && !LoadDictionary(input_buffer)) {
87 return SDCH_STREAM_ERROR;
88 }
89
90 // If there's any data left in |buffered_output_|, try flushing some of it.
91 if (Flush(output_buffer, output_buffer_size, bytes_read) !=
92 SDCH_STREAM_MORE_INPUT)
93 return SDCH_STREAM_MORE_OUTPUT_SPACE;
94
95 // If LoadDictionary() consume all |input_buffer|, return early here.
96 if (input_buffer->BytesRemaining() == 0)
97 return SDCH_STREAM_MORE_INPUT;
98
99 DCHECK(dictionary_tried_load_);
100
101 // If output has been replaced via ReplaceOutput, short-circuit any other
102 // processing, and emit as much of the buffered output as possible. If there's
103 // no buffered output left, return EOF.
104 if (output_replaced_)
105 return Flush(output_buffer, output_buffer_size, bytes_read);
106
107 // If this stream is no longer decoding, pass any input through.
108 if (passthrough_)
109 return PassThrough(output_buffer, output_buffer_size, input_buffer,
110 bytes_read);
111
112 // There is no data left in |buffered_output_| and there is data in
113 // |input_buffer|, so run the vcdiff decoder.
114 DCHECK(buffered_output_.empty());
115 DCHECK_LT(0, input_buffer->BytesRemaining());
116
117 // Calls to DecodeChunk always consume all their input, so this always drains
118 // the entire buffer.
119 bool ok = decoder_->DecodeChunk(
120 input_buffer->data(), input_buffer->BytesRemaining(), &buffered_output_);
121
122 input_buffer->DidConsume(input_buffer->BytesRemaining());
123
124 if (!ok) {
125 // Don't call it again.
126 decoder_.reset();
127 return SDCH_STREAM_ERROR;
128 }
129 return Flush(output_buffer, output_buffer_size, bytes_read);
130 }
131
132 SdchSourceStream::SdchStreamState SdchSourceStream::PassThrough(
133 IOBuffer* output_buffer,
134 size_t output_buffer_size,
135 DrainableIOBuffer* input_buffer,
136 size_t* bytes_read) {
137 *bytes_read = 0;
138 SdchStreamState state = Flush(output_buffer, output_buffer_size, bytes_read);
139 if (state == SDCH_STREAM_MORE_OUTPUT_SPACE)
140 return state;
141
142 // No buffered data to flush. See if there's any input.
143 DCHECK(buffered_output_.empty());
144 if (input_buffer->BytesRemaining() == 0)
145 return SDCH_STREAM_MORE_INPUT;
146
147 size_t to_copy = output_buffer_size;
148 if (to_copy > base::checked_cast<size_t>(input_buffer->BytesRemaining()))
149 to_copy = input_buffer->BytesRemaining();
150 memcpy(output_buffer->data(), input_buffer->data(), to_copy);
151 input_buffer->DidConsume(to_copy);
152 *bytes_read = *bytes_read + to_copy;
153 if (input_buffer->BytesRemaining() > 0)
154 return SDCH_STREAM_MORE_OUTPUT_SPACE;
155 else
156 return SDCH_STREAM_MORE_INPUT;
157 }
158
159 SdchSourceStream::SdchStreamState SdchSourceStream::Flush(
160 IOBuffer* output_buffer,
161 size_t output_buffer_size,
162 size_t* bytes_read) {
163 size_t to_copy = buffered_output_.length();
164 if (to_copy > output_buffer_size)
165 to_copy = output_buffer_size;
166 memcpy(output_buffer->data(), buffered_output_.data(), to_copy);
167 buffered_output_.erase(0, to_copy);
168 *bytes_read = to_copy;
169 if (!buffered_output_.empty()) {
170 return SDCH_STREAM_MORE_OUTPUT_SPACE;
171 }
172 return SDCH_STREAM_MORE_INPUT;
173 }
174
175 // Attempts to find a dictionary ID at the start of the input stream and load
176 // the dictionary with that identifier. Returns true if no error happened or an
177 // error happened but the delegate repaired it, and false if an error happened
178 // and the delegate did not repair it.
179 bool SdchSourceStream::LoadDictionary(DrainableIOBuffer* input_buffer) {
180 // Copy at most |kServerIdLength| from |input_buffer|.
181 size_t to_copy = kServerIdLength - dictionary_id_.length();
182 if (to_copy > base::checked_cast<size_t>(input_buffer->BytesRemaining()))
183 to_copy = input_buffer->BytesRemaining();
184 dictionary_id_.append(input_buffer->data(), to_copy);
185 input_buffer->DidConsume(to_copy);
186
187 // Not enough bytes for a dictionary ID accumulated yet.
188 if (dictionary_id_.length() != kServerIdLength)
189 return true;
190
191 dictionary_tried_load_ = true;
192
193 // If the dictionary ID looks bogus, or the delegate can't find a dictionary
194 // with that identifier, then call OnDictionaryError and bail.
195 // Also, stick the dictionary ID into the output buffer here; LoadDictionary
196 // will consume it assuming it's a dictionary ID, so if it's really not, the
197 // "dictionary ID" should appear in the output stream.
198 //
199 // To avoid passing a std::string with a null terminator into GetDictionary(),
200 // |server_hash| here removes the last byte blindly, and this method only
201 // calls
202 // GetDictionary with it if CouldBeDictionaryId returns true.
203 std::string server_hash = dictionary_id_.substr(0, kServerIdLength - 1);
204 const std::string* dictionary_text = nullptr;
205 if (!CouldBeDictionaryId(dictionary_id_) ||
206 !delegate_->OnGetDictionary(server_hash, &dictionary_text)) {
207 input_buffer->DidConsume(input_buffer->BytesRemaining());
208 buffered_output_.append(dictionary_id_);
209 return AskDelegateToHandleDictionaryError();
210 }
211
212 decoder_.reset(new open_vcdiff::VCDiffStreamingDecoder);
213 decoder_->SetAllowVcdTarget(false);
214 decoder_->StartDecoding(dictionary_text->data(), dictionary_text->length());
215 return true;
216 }
217
218 bool SdchSourceStream::CouldBeDictionaryId(const std::string& id) const {
219 for (size_t i = 0; i < kServerIdLength - 1; i++) {
220 char base64_char = id[i];
221 if (!isalnum(base64_char) && '-' != base64_char && '_' != base64_char)
222 return false;
223 }
224 if (id[kServerIdLength - 1] != '\0')
225 return false;
226 return true;
227 }
228
229 bool SdchSourceStream::AskDelegateToHandleDictionaryError() {
230 base::AutoReset<bool> resetter(&in_delegate_handler_, true);
231 return delegate_->OnDictionaryError(this);
232 }
233
234 bool SdchSourceStream::AskDelegateToHandleDecodingError() {
235 base::AutoReset<bool> resetter(&in_delegate_handler_, true);
236 return delegate_->OnDecodingError(this);
237 }
238
239 } // namespace net
OLDNEW
« no previous file with comments | « net/filter/sdch_source_stream.h ('k') | net/filter/sdch_source_stream_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698