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_lite.h>
36#include <google/protobuf/stubs/common.h>
37#include <google/protobuf/stubs/stl_util.h>
38
39#include <algorithm>
40
41namespace google {
42namespace protobuf {
43namespace io {
44
45namespace {
46
47// Default block size for Copying{In,Out}putStreamAdaptor.
48static const int kDefaultBlockSize = 8192;
49
50}  // namespace
51
52// ===================================================================
53
54ArrayInputStream::ArrayInputStream(const void* data, int size,
55                                   int block_size)
56  : data_(reinterpret_cast<const uint8*>(data)),
57    size_(size),
58    block_size_(block_size > 0 ? block_size : size),
59    position_(0),
60    last_returned_size_(0) {
61}
62
63ArrayInputStream::~ArrayInputStream() {
64}
65
66bool ArrayInputStream::Next(const void** data, int* size) {
67  if (position_ < size_) {
68    last_returned_size_ = min(block_size_, size_ - position_);
69    *data = data_ + position_;
70    *size = last_returned_size_;
71    position_ += last_returned_size_;
72    return true;
73  } else {
74    // We're at the end of the array.
75    last_returned_size_ = 0;   // Don't let caller back up.
76    return false;
77  }
78}
79
80void ArrayInputStream::BackUp(int count) {
81  GOOGLE_CHECK_GT(last_returned_size_, 0)
82      << "BackUp() can only be called after a successful Next().";
83  GOOGLE_CHECK_LE(count, last_returned_size_);
84  GOOGLE_CHECK_GE(count, 0);
85  position_ -= count;
86  last_returned_size_ = 0;  // Don't let caller back up further.
87}
88
89bool ArrayInputStream::Skip(int count) {
90  GOOGLE_CHECK_GE(count, 0);
91  last_returned_size_ = 0;   // Don't let caller back up.
92  if (count > size_ - position_) {
93    position_ = size_;
94    return false;
95  } else {
96    position_ += count;
97    return true;
98  }
99}
100
101int64 ArrayInputStream::ByteCount() const {
102  return position_;
103}
104
105
106// ===================================================================
107
108ArrayOutputStream::ArrayOutputStream(void* data, int size, int block_size)
109  : data_(reinterpret_cast<uint8*>(data)),
110    size_(size),
111    block_size_(block_size > 0 ? block_size : size),
112    position_(0),
113    last_returned_size_(0) {
114}
115
116ArrayOutputStream::~ArrayOutputStream() {
117}
118
119bool ArrayOutputStream::Next(void** data, int* size) {
120  if (position_ < size_) {
121    last_returned_size_ = min(block_size_, size_ - position_);
122    *data = data_ + position_;
123    *size = last_returned_size_;
124    position_ += last_returned_size_;
125    return true;
126  } else {
127    // We're at the end of the array.
128    last_returned_size_ = 0;   // Don't let caller back up.
129    return false;
130  }
131}
132
133void ArrayOutputStream::BackUp(int count) {
134  GOOGLE_CHECK_GT(last_returned_size_, 0)
135      << "BackUp() can only be called after a successful Next().";
136  GOOGLE_CHECK_LE(count, last_returned_size_);
137  GOOGLE_CHECK_GE(count, 0);
138  position_ -= count;
139  last_returned_size_ = 0;  // Don't let caller back up further.
140}
141
142int64 ArrayOutputStream::ByteCount() const {
143  return position_;
144}
145
146// ===================================================================
147
148StringOutputStream::StringOutputStream(string* target)
149  : target_(target) {
150}
151
152StringOutputStream::~StringOutputStream() {
153}
154
155bool StringOutputStream::Next(void** data, int* size) {
156  int old_size = target_->size();
157
158  // Grow the string.
159  if (old_size < target_->capacity()) {
160    // Resize the string to match its capacity, since we can get away
161    // without a memory allocation this way.
162    STLStringResizeUninitialized(target_, target_->capacity());
163  } else {
164    // Size has reached capacity, so double the size.  Also make sure
165    // that the new size is at least kMinimumSize.
166    STLStringResizeUninitialized(
167      target_,
168      max(old_size * 2,
169          kMinimumSize + 0));  // "+ 0" works around GCC4 weirdness.
170  }
171
172  *data = string_as_array(target_) + old_size;
173  *size = target_->size() - old_size;
174  return true;
175}
176
177void StringOutputStream::BackUp(int count) {
178  GOOGLE_CHECK_GE(count, 0);
179  GOOGLE_CHECK_LE(count, target_->size());
180  target_->resize(target_->size() - count);
181}
182
183int64 StringOutputStream::ByteCount() const {
184  return target_->size();
185}
186
187// ===================================================================
188
189CopyingInputStream::~CopyingInputStream() {}
190
191int CopyingInputStream::Skip(int count) {
192  char junk[4096];
193  int skipped = 0;
194  while (skipped < count) {
195    int bytes = Read(junk, min(count - skipped,
196                               implicit_cast<int>(sizeof(junk))));
197    if (bytes <= 0) {
198      // EOF or read error.
199      return skipped;
200    }
201    skipped += bytes;
202  }
203  return skipped;
204}
205
206CopyingInputStreamAdaptor::CopyingInputStreamAdaptor(
207    CopyingInputStream* copying_stream, int block_size)
208  : copying_stream_(copying_stream),
209    owns_copying_stream_(false),
210    failed_(false),
211    position_(0),
212    buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
213    buffer_used_(0),
214    backup_bytes_(0) {
215}
216
217CopyingInputStreamAdaptor::~CopyingInputStreamAdaptor() {
218  if (owns_copying_stream_) {
219    delete copying_stream_;
220  }
221}
222
223bool CopyingInputStreamAdaptor::Next(const void** data, int* size) {
224  if (failed_) {
225    // Already failed on a previous read.
226    return false;
227  }
228
229  AllocateBufferIfNeeded();
230
231  if (backup_bytes_ > 0) {
232    // We have data left over from a previous BackUp(), so just return that.
233    *data = buffer_.get() + buffer_used_ - backup_bytes_;
234    *size = backup_bytes_;
235    backup_bytes_ = 0;
236    return true;
237  }
238
239  // Read new data into the buffer.
240  buffer_used_ = copying_stream_->Read(buffer_.get(), buffer_size_);
241  if (buffer_used_ <= 0) {
242    // EOF or read error.  We don't need the buffer anymore.
243    if (buffer_used_ < 0) {
244      // Read error (not EOF).
245      failed_ = true;
246    }
247    FreeBuffer();
248    return false;
249  }
250  position_ += buffer_used_;
251
252  *size = buffer_used_;
253  *data = buffer_.get();
254  return true;
255}
256
257void CopyingInputStreamAdaptor::BackUp(int count) {
258  GOOGLE_CHECK(backup_bytes_ == 0 && buffer_.get() != NULL)
259    << " BackUp() can only be called after Next().";
260  GOOGLE_CHECK_LE(count, buffer_used_)
261    << " Can't back up over more bytes than were returned by the last call"
262       " to Next().";
263  GOOGLE_CHECK_GE(count, 0)
264    << " Parameter to BackUp() can't be negative.";
265
266  backup_bytes_ = count;
267}
268
269bool CopyingInputStreamAdaptor::Skip(int count) {
270  GOOGLE_CHECK_GE(count, 0);
271
272  if (failed_) {
273    // Already failed on a previous read.
274    return false;
275  }
276
277  // First skip any bytes left over from a previous BackUp().
278  if (backup_bytes_ >= count) {
279    // We have more data left over than we're trying to skip.  Just chop it.
280    backup_bytes_ -= count;
281    return true;
282  }
283
284  count -= backup_bytes_;
285  backup_bytes_ = 0;
286
287  int skipped = copying_stream_->Skip(count);
288  position_ += skipped;
289  return skipped == count;
290}
291
292int64 CopyingInputStreamAdaptor::ByteCount() const {
293  return position_ - backup_bytes_;
294}
295
296void CopyingInputStreamAdaptor::AllocateBufferIfNeeded() {
297  if (buffer_.get() == NULL) {
298    buffer_.reset(new uint8[buffer_size_]);
299  }
300}
301
302void CopyingInputStreamAdaptor::FreeBuffer() {
303  GOOGLE_CHECK_EQ(backup_bytes_, 0);
304  buffer_used_ = 0;
305  buffer_.reset();
306}
307
308// ===================================================================
309
310CopyingOutputStream::~CopyingOutputStream() {}
311
312CopyingOutputStreamAdaptor::CopyingOutputStreamAdaptor(
313    CopyingOutputStream* copying_stream, int block_size)
314  : copying_stream_(copying_stream),
315    owns_copying_stream_(false),
316    failed_(false),
317    position_(0),
318    buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize),
319    buffer_used_(0) {
320}
321
322CopyingOutputStreamAdaptor::~CopyingOutputStreamAdaptor() {
323  WriteBuffer();
324  if (owns_copying_stream_) {
325    delete copying_stream_;
326  }
327}
328
329bool CopyingOutputStreamAdaptor::Flush() {
330  return WriteBuffer();
331}
332
333bool CopyingOutputStreamAdaptor::Next(void** data, int* size) {
334  if (buffer_used_ == buffer_size_) {
335    if (!WriteBuffer()) return false;
336  }
337
338  AllocateBufferIfNeeded();
339
340  *data = buffer_.get() + buffer_used_;
341  *size = buffer_size_ - buffer_used_;
342  buffer_used_ = buffer_size_;
343  return true;
344}
345
346void CopyingOutputStreamAdaptor::BackUp(int count) {
347  GOOGLE_CHECK_GE(count, 0);
348  GOOGLE_CHECK_EQ(buffer_used_, buffer_size_)
349    << " BackUp() can only be called after Next().";
350  GOOGLE_CHECK_LE(count, buffer_used_)
351    << " Can't back up over more bytes than were returned by the last call"
352       " to Next().";
353
354  buffer_used_ -= count;
355}
356
357int64 CopyingOutputStreamAdaptor::ByteCount() const {
358  return position_ + buffer_used_;
359}
360
361bool CopyingOutputStreamAdaptor::WriteBuffer() {
362  if (failed_) {
363    // Already failed on a previous write.
364    return false;
365  }
366
367  if (buffer_used_ == 0) return true;
368
369  if (copying_stream_->Write(buffer_.get(), buffer_used_)) {
370    position_ += buffer_used_;
371    buffer_used_ = 0;
372    return true;
373  } else {
374    failed_ = true;
375    FreeBuffer();
376    return false;
377  }
378}
379
380void CopyingOutputStreamAdaptor::AllocateBufferIfNeeded() {
381  if (buffer_ == NULL) {
382    buffer_.reset(new uint8[buffer_size_]);
383  }
384}
385
386void CopyingOutputStreamAdaptor::FreeBuffer() {
387  buffer_used_ = 0;
388  buffer_.reset();
389}
390
391// ===================================================================
392
393}  // namespace io
394}  // namespace protobuf
395}  // namespace google
396