zero_copy_stream_impl_lite.cc revision fbaaef999ba563838ebd00874ed8a1c01fbf286d
1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// http://code.google.com/p/protobuf/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9//     * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11//     * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15//     * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: kenton@google.com (Kenton Varda)
32//  Based on original Protocol Buffers design by
33//  Sanjay Ghemawat, Jeff Dean, and others.
34
35#include <google/protobuf/io/zero_copy_stream_impl.h>
36#include <google/protobuf/stubs/common.h>
37#include <google/protobuf/stubs/stl_util-inl.h>
38
39namespace google {
40namespace protobuf {
41namespace io {
42
43namespace {
44
45// Default block size for Copying{In,Out}putStreamAdaptor.
46static const int kDefaultBlockSize = 8192;
47
48}  // namespace
49
50// ===================================================================
51
52ArrayInputStream::ArrayInputStream(const void* data, int size,
53                                   int block_size)
54  : data_(reinterpret_cast<const uint8*>(data)),
55    size_(size),
56    block_size_(block_size > 0 ? block_size : size),
57    position_(0),
58    last_returned_size_(0) {
59}
60
61ArrayInputStream::~ArrayInputStream() {
62}
63
64bool ArrayInputStream::Next(const void** data, int* size) {
65  if (position_ < size_) {
66    last_returned_size_ = min(block_size_, size_ - position_);
67    *data = data_ + position_;
68    *size = last_returned_size_;
69    position_ += last_returned_size_;
70    return true;
71  } else {
72    // We're at the end of the array.
73    last_returned_size_ = 0;   // Don't let caller back up.
74    return false;
75  }
76}
77
78void ArrayInputStream::BackUp(int count) {
79  GOOGLE_CHECK_GT(last_returned_size_, 0)
80      << "BackUp() can only be called after a successful Next().";
81  GOOGLE_CHECK_LE(count, last_returned_size_);
82  GOOGLE_CHECK_GE(count, 0);
83  position_ -= count;
84  last_returned_size_ = 0;  // Don't let caller back up further.
85}
86
87bool ArrayInputStream::Skip(int count) {
88  GOOGLE_CHECK_GE(count, 0);
89  last_returned_size_ = 0;   // Don't let caller back up.
90  if (count > size_ - position_) {
91    position_ = size_;
92    return false;
93  } else {
94    position_ += count;
95    return true;
96  }
97}
98
99int64 ArrayInputStream::ByteCount() const {
100  return position_;
101}
102
103
104// ===================================================================
105
106ArrayOutputStream::ArrayOutputStream(void* data, int size, int block_size)
107  : data_(reinterpret_cast<uint8*>(data)),
108    size_(size),
109    block_size_(block_size > 0 ? block_size : size),
110    position_(0),
111    last_returned_size_(0) {
112}
113
114ArrayOutputStream::~ArrayOutputStream() {
115}
116
117bool ArrayOutputStream::Next(void** data, int* size) {
118  if (position_ < size_) {
119    last_returned_size_ = min(block_size_, size_ - position_);
120    *data = data_ + position_;
121    *size = last_returned_size_;
122    position_ += last_returned_size_;
123    return true;
124  } else {
125    // We're at the end of the array.
126    last_returned_size_ = 0;   // Don't let caller back up.
127    return false;
128  }
129}
130
131void ArrayOutputStream::BackUp(int count) {
132  GOOGLE_CHECK_GT(last_returned_size_, 0)
133      << "BackUp() can only be called after a successful Next().";
134  GOOGLE_CHECK_LE(count, last_returned_size_);
135  GOOGLE_CHECK_GE(count, 0);
136  position_ -= count;
137  last_returned_size_ = 0;  // Don't let caller back up further.
138}
139
140int64 ArrayOutputStream::ByteCount() const {
141  return position_;
142}
143
144// ===================================================================
145
146StringOutputStream::StringOutputStream(string* target)
147  : target_(target) {
148}
149
150StringOutputStream::~StringOutputStream() {
151}
152
153bool StringOutputStream::Next(void** data, int* size) {
154  int old_size = target_->size();
155
156  // Grow the string.
157  if (old_size < target_->capacity()) {
158    // Resize the string to match its capacity, since we can get away
159    // without a memory allocation this way.
160    STLStringResizeUninitialized(target_, target_->capacity());
161  } else {
162    // Size has reached capacity, so double the size.  Also make sure
163    // that the new size is at least kMinimumSize.
164    STLStringResizeUninitialized(
165      target_,
166      max(old_size * 2,
167          kMinimumSize + 0));  // "+ 0" works around GCC4 weirdness.
168  }
169
170  *data = string_as_array(target_) + old_size;
171  *size = target_->size() - old_size;
172  return true;
173}
174
175void StringOutputStream::BackUp(int count) {
176  GOOGLE_CHECK_GE(count, 0);
177  GOOGLE_CHECK_LE(count, target_->size());
178  target_->resize(target_->size() - count);
179}
180
181int64 StringOutputStream::ByteCount() const {
182  return target_->size();
183}
184
185// ===================================================================
186
187CopyingInputStream::~CopyingInputStream() {}
188
189int CopyingInputStream::Skip(int count) {
190  char junk[4096];
191  int skipped = 0;
192  while (skipped < count) {
193    int bytes = Read(junk, min(count - skipped,
194                               implicit_cast<int>(sizeof(junk))));
195    if (bytes <= 0) {
196      // EOF or read error.
197      return skipped;
198    }
199    skipped += bytes;
200  }
201  return skipped;
202}
203
204CopyingInputStreamAdaptor::CopyingInputStreamAdaptor(
205    CopyingInputStream* copying_stream, int block_size)
206  : copying_stream_(copying_stream),
207    owns_copying_stream_(false),
208    failed_(false),
209    position_(0),
210    buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
211    buffer_used_(0),
212    backup_bytes_(0) {
213}
214
215CopyingInputStreamAdaptor::~CopyingInputStreamAdaptor() {
216  if (owns_copying_stream_) {
217    delete copying_stream_;
218  }
219}
220
221bool CopyingInputStreamAdaptor::Next(const void** data, int* size) {
222  if (failed_) {
223    // Already failed on a previous read.
224    return false;
225  }
226
227  AllocateBufferIfNeeded();
228
229  if (backup_bytes_ > 0) {
230    // We have data left over from a previous BackUp(), so just return that.
231    *data = buffer_.get() + buffer_used_ - backup_bytes_;
232    *size = backup_bytes_;
233    backup_bytes_ = 0;
234    return true;
235  }
236
237  // Read new data into the buffer.
238  buffer_used_ = copying_stream_->Read(buffer_.get(), buffer_size_);
239  if (buffer_used_ <= 0) {
240    // EOF or read error.  We don't need the buffer anymore.
241    if (buffer_used_ < 0) {
242      // Read error (not EOF).
243      failed_ = true;
244    }
245    FreeBuffer();
246    return false;
247  }
248  position_ += buffer_used_;
249
250  *size = buffer_used_;
251  *data = buffer_.get();
252  return true;
253}
254
255void CopyingInputStreamAdaptor::BackUp(int count) {
256  GOOGLE_CHECK(backup_bytes_ == 0 && buffer_.get() != NULL)
257    << " BackUp() can only be called after Next().";
258  GOOGLE_CHECK_LE(count, buffer_used_)
259    << " Can't back up over more bytes than were returned by the last call"
260       " to Next().";
261  GOOGLE_CHECK_GE(count, 0)
262    << " Parameter to BackUp() can't be negative.";
263
264  backup_bytes_ = count;
265}
266
267bool CopyingInputStreamAdaptor::Skip(int count) {
268  GOOGLE_CHECK_GE(count, 0);
269
270  if (failed_) {
271    // Already failed on a previous read.
272    return false;
273  }
274
275  // First skip any bytes left over from a previous BackUp().
276  if (backup_bytes_ >= count) {
277    // We have more data left over than we're trying to skip.  Just chop it.
278    backup_bytes_ -= count;
279    return true;
280  }
281
282  count -= backup_bytes_;
283  backup_bytes_ = 0;
284
285  int skipped = copying_stream_->Skip(count);
286  position_ += skipped;
287  return skipped == count;
288}
289
290int64 CopyingInputStreamAdaptor::ByteCount() const {
291  return position_ - backup_bytes_;
292}
293
294void CopyingInputStreamAdaptor::AllocateBufferIfNeeded() {
295  if (buffer_.get() == NULL) {
296    buffer_.reset(new uint8[buffer_size_]);
297  }
298}
299
300void CopyingInputStreamAdaptor::FreeBuffer() {
301  GOOGLE_CHECK_EQ(backup_bytes_, 0);
302  buffer_used_ = 0;
303  buffer_.reset();
304}
305
306// ===================================================================
307
308CopyingOutputStream::~CopyingOutputStream() {}
309
310CopyingOutputStreamAdaptor::CopyingOutputStreamAdaptor(
311    CopyingOutputStream* copying_stream, int block_size)
312  : copying_stream_(copying_stream),
313    owns_copying_stream_(false),
314    failed_(false),
315    position_(0),
316    buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
317    buffer_used_(0) {
318}
319
320CopyingOutputStreamAdaptor::~CopyingOutputStreamAdaptor() {
321  WriteBuffer();
322  if (owns_copying_stream_) {
323    delete copying_stream_;
324  }
325}
326
327bool CopyingOutputStreamAdaptor::Flush() {
328  return WriteBuffer();
329}
330
331bool CopyingOutputStreamAdaptor::Next(void** data, int* size) {
332  if (buffer_used_ == buffer_size_) {
333    if (!WriteBuffer()) return false;
334  }
335
336  AllocateBufferIfNeeded();
337
338  *data = buffer_.get() + buffer_used_;
339  *size = buffer_size_ - buffer_used_;
340  buffer_used_ = buffer_size_;
341  return true;
342}
343
344void CopyingOutputStreamAdaptor::BackUp(int count) {
345  GOOGLE_CHECK_GE(count, 0);
346  GOOGLE_CHECK_EQ(buffer_used_, buffer_size_)
347    << " BackUp() can only be called after Next().";
348  GOOGLE_CHECK_LE(count, buffer_used_)
349    << " Can't back up over more bytes than were returned by the last call"
350       " to Next().";
351
352  buffer_used_ -= count;
353}
354
355int64 CopyingOutputStreamAdaptor::ByteCount() const {
356  return position_ + buffer_used_;
357}
358
359bool CopyingOutputStreamAdaptor::WriteBuffer() {
360  if (failed_) {
361    // Already failed on a previous write.
362    return false;
363  }
364
365  if (buffer_used_ == 0) return true;
366
367  if (copying_stream_->Write(buffer_.get(), buffer_used_)) {
368    position_ += buffer_used_;
369    buffer_used_ = 0;
370    return true;
371  } else {
372    failed_ = true;
373    FreeBuffer();
374    return false;
375  }
376}
377
378void CopyingOutputStreamAdaptor::AllocateBufferIfNeeded() {
379  if (buffer_ == NULL) {
380    buffer_.reset(new uint8[buffer_size_]);
381  }
382}
383
384void CopyingOutputStreamAdaptor::FreeBuffer() {
385  buffer_used_ = 0;
386  buffer_.reset();
387}
388
389// ===================================================================
390
391}  // namespace io
392}  // namespace protobuf
393}  // namespace google
394