• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
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 // Testing strategy:  For each type of I/O (array, string, file, etc.) we
36 // create an output stream and write some data to it, then create a
37 // corresponding input stream to read the same data back and expect it to
38 // match.  When the data is written, it is written in several small chunks
39 // of varying sizes, with a BackUp() after each chunk.  It is read back
40 // similarly, but with chunks separated at different points.  The whole
41 // process is run with a variety of block sizes for both the input and
42 // the output.
43 //
44 // TODO(kenton):  Rewrite this test to bring it up to the standards of all
45 //   the other proto2 tests.  May want to wait for gTest to implement
46 //   "parametized tests" so that one set of tests can be used on all the
47 //   implementations.
48 
49 
50 #ifndef _MSC_VER
51 #include <unistd.h>
52 #endif
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <stdlib.h>
56 #include <sys/stat.h>
57 #include <sys/types.h>
58 
59 #include <memory>
60 #include <sstream>
61 
62 #include <google/protobuf/testing/file.h>
63 #include <google/protobuf/test_util2.h>
64 #include <google/protobuf/io/coded_stream.h>
65 #include <google/protobuf/io/io_win32.h>
66 #include <google/protobuf/io/zero_copy_stream_impl.h>
67 
68 #if HAVE_ZLIB
69 #include <google/protobuf/io/gzip_stream.h>
70 #endif
71 
72 #include <google/protobuf/stubs/common.h>
73 #include <google/protobuf/stubs/logging.h>
74 #include <google/protobuf/testing/file.h>
75 #include <google/protobuf/testing/googletest.h>
76 #include <gtest/gtest.h>
77 
78 namespace google {
79 namespace protobuf {
80 namespace io {
81 namespace {
82 
83 #ifdef _WIN32
84 #define pipe(fds) _pipe(fds, 4096, O_BINARY)
85 // DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
86 // them like we do below.
87 using google::protobuf::io::win32::access;
88 using google::protobuf::io::win32::close;
89 using google::protobuf::io::win32::mkdir;
90 using google::protobuf::io::win32::open;
91 #endif
92 
93 #ifndef O_BINARY
94 #ifdef _O_BINARY
95 #define O_BINARY _O_BINARY
96 #else
97 #define O_BINARY 0  // If this isn't defined, the platform doesn't need it.
98 #endif
99 #endif
100 
101 class IoTest : public testing::Test {
102  protected:
103   // Test helpers.
104 
105   // Helper to write an array of data to an output stream.
106   bool WriteToOutput(ZeroCopyOutputStream* output, const void* data, int size);
107   // Helper to read a fixed-length array of data from an input stream.
108   int ReadFromInput(ZeroCopyInputStream* input, void* data, int size);
109   // Write a string to the output stream.
110   void WriteString(ZeroCopyOutputStream* output, const std::string& str);
111   // Read a number of bytes equal to the size of the given string and checks
112   // that it matches the string.
113   void ReadString(ZeroCopyInputStream* input, const std::string& str);
114   // Writes some text to the output stream in a particular order.  Returns
115   // the number of bytes written, in case the caller needs that to set up an
116   // input stream.
117   int WriteStuff(ZeroCopyOutputStream* output);
118   // Reads text from an input stream and expects it to match what
119   // WriteStuff() writes.
120   void ReadStuff(ZeroCopyInputStream* input);
121 
122   // Similar to WriteStuff, but performs more sophisticated testing.
123   int WriteStuffLarge(ZeroCopyOutputStream* output);
124   // Reads and tests a stream that should have been written to
125   // via WriteStuffLarge().
126   void ReadStuffLarge(ZeroCopyInputStream* input);
127 
128 #if HAVE_ZLIB
129   std::string Compress(const std::string& data,
130                        const GzipOutputStream::Options& options);
131   std::string Uncompress(const std::string& data);
132 #endif
133 
134   static const int kBlockSizes[];
135   static const int kBlockSizeCount;
136 };
137 
138 const int IoTest::kBlockSizes[] = {-1, 1, 2, 5, 7, 10, 23, 64};
139 const int IoTest::kBlockSizeCount = GOOGLE_ARRAYSIZE(IoTest::kBlockSizes);
140 
WriteToOutput(ZeroCopyOutputStream * output,const void * data,int size)141 bool IoTest::WriteToOutput(ZeroCopyOutputStream* output, const void* data,
142                            int size) {
143   const uint8* in = reinterpret_cast<const uint8*>(data);
144   int in_size = size;
145 
146   void* out;
147   int out_size;
148 
149   while (true) {
150     if (!output->Next(&out, &out_size)) {
151       return false;
152     }
153     EXPECT_GT(out_size, 0);
154 
155     if (in_size <= out_size) {
156       memcpy(out, in, in_size);
157       output->BackUp(out_size - in_size);
158       return true;
159     }
160 
161     memcpy(out, in, out_size);
162     in += out_size;
163     in_size -= out_size;
164   }
165 }
166 
167 #define MAX_REPEATED_ZEROS 100
168 
ReadFromInput(ZeroCopyInputStream * input,void * data,int size)169 int IoTest::ReadFromInput(ZeroCopyInputStream* input, void* data, int size) {
170   uint8* out = reinterpret_cast<uint8*>(data);
171   int out_size = size;
172 
173   const void* in;
174   int in_size = 0;
175 
176   int repeated_zeros = 0;
177 
178   while (true) {
179     if (!input->Next(&in, &in_size)) {
180       return size - out_size;
181     }
182     EXPECT_GT(in_size, -1);
183     if (in_size == 0) {
184       repeated_zeros++;
185     } else {
186       repeated_zeros = 0;
187     }
188     EXPECT_LT(repeated_zeros, MAX_REPEATED_ZEROS);
189 
190     if (out_size <= in_size) {
191       memcpy(out, in, out_size);
192       if (in_size > out_size) {
193         input->BackUp(in_size - out_size);
194       }
195       return size;  // Copied all of it.
196     }
197 
198     memcpy(out, in, in_size);
199     out += in_size;
200     out_size -= in_size;
201   }
202 }
203 
WriteString(ZeroCopyOutputStream * output,const std::string & str)204 void IoTest::WriteString(ZeroCopyOutputStream* output, const std::string& str) {
205   EXPECT_TRUE(WriteToOutput(output, str.c_str(), str.size()));
206 }
207 
ReadString(ZeroCopyInputStream * input,const std::string & str)208 void IoTest::ReadString(ZeroCopyInputStream* input, const std::string& str) {
209   std::unique_ptr<char[]> buffer(new char[str.size() + 1]);
210   buffer[str.size()] = '\0';
211   EXPECT_EQ(ReadFromInput(input, buffer.get(), str.size()), str.size());
212   EXPECT_STREQ(str.c_str(), buffer.get());
213 }
214 
WriteStuff(ZeroCopyOutputStream * output)215 int IoTest::WriteStuff(ZeroCopyOutputStream* output) {
216   WriteString(output, "Hello world!\n");
217   WriteString(output, "Some te");
218   WriteString(output, "xt.  Blah blah.");
219   WriteString(output, "abcdefg");
220   WriteString(output, "01234567890123456789");
221   WriteString(output, "foobar");
222 
223   EXPECT_EQ(output->ByteCount(), 68);
224 
225   int result = output->ByteCount();
226   return result;
227 }
228 
229 // Reads text from an input stream and expects it to match what WriteStuff()
230 // writes.
ReadStuff(ZeroCopyInputStream * input)231 void IoTest::ReadStuff(ZeroCopyInputStream* input) {
232   ReadString(input, "Hello world!\n");
233   ReadString(input, "Some text.  ");
234   ReadString(input, "Blah ");
235   ReadString(input, "blah.");
236   ReadString(input, "abcdefg");
237   EXPECT_TRUE(input->Skip(20));
238   ReadString(input, "foo");
239   ReadString(input, "bar");
240 
241   EXPECT_EQ(input->ByteCount(), 68);
242 
243   uint8 byte;
244   EXPECT_EQ(ReadFromInput(input, &byte, 1), 0);
245 }
246 
WriteStuffLarge(ZeroCopyOutputStream * output)247 int IoTest::WriteStuffLarge(ZeroCopyOutputStream* output) {
248   WriteString(output, "Hello world!\n");
249   WriteString(output, "Some te");
250   WriteString(output, "xt.  Blah blah.");
251   WriteString(output, std::string(100000, 'x'));  // A very long string
252   WriteString(output, std::string(100000, 'y'));  // A very long string
253   WriteString(output, "01234567890123456789");
254 
255   EXPECT_EQ(output->ByteCount(), 200055);
256 
257   int result = output->ByteCount();
258   return result;
259 }
260 
261 // Reads text from an input stream and expects it to match what WriteStuff()
262 // writes.
ReadStuffLarge(ZeroCopyInputStream * input)263 void IoTest::ReadStuffLarge(ZeroCopyInputStream* input) {
264   ReadString(input, "Hello world!\nSome text.  ");
265   EXPECT_TRUE(input->Skip(5));
266   ReadString(input, "blah.");
267   EXPECT_TRUE(input->Skip(100000 - 10));
268   ReadString(input, std::string(10, 'x') + std::string(100000 - 20000, 'y'));
269   EXPECT_TRUE(input->Skip(20000 - 10));
270   ReadString(input, "yyyyyyyyyy01234567890123456789");
271 
272   EXPECT_EQ(input->ByteCount(), 200055);
273 
274   uint8 byte;
275   EXPECT_EQ(ReadFromInput(input, &byte, 1), 0);
276 }
277 
278 // ===================================================================
279 
TEST_F(IoTest,ArrayIo)280 TEST_F(IoTest, ArrayIo) {
281   const int kBufferSize = 256;
282   uint8 buffer[kBufferSize];
283 
284   for (int i = 0; i < kBlockSizeCount; i++) {
285     for (int j = 0; j < kBlockSizeCount; j++) {
286       int size;
287       {
288         ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
289         size = WriteStuff(&output);
290       }
291       {
292         ArrayInputStream input(buffer, size, kBlockSizes[j]);
293         ReadStuff(&input);
294       }
295     }
296   }
297 }
298 
TEST_F(IoTest,TwoSessionWrite)299 TEST_F(IoTest, TwoSessionWrite) {
300   // Test that two concatenated write sessions read correctly
301 
302   static const char* strA = "0123456789";
303   static const char* strB = "WhirledPeas";
304   const int kBufferSize = 2 * 1024;
305   uint8* buffer = new uint8[kBufferSize];
306   char* temp_buffer = new char[40];
307 
308   for (int i = 0; i < kBlockSizeCount; i++) {
309     for (int j = 0; j < kBlockSizeCount; j++) {
310       ArrayOutputStream* output =
311           new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]);
312       CodedOutputStream* coded_output = new CodedOutputStream(output);
313       coded_output->WriteVarint32(strlen(strA));
314       coded_output->WriteRaw(strA, strlen(strA));
315       delete coded_output;  // flush
316       int64 pos = output->ByteCount();
317       delete output;
318       output = new ArrayOutputStream(buffer + pos, kBufferSize - pos,
319                                      kBlockSizes[i]);
320       coded_output = new CodedOutputStream(output);
321       coded_output->WriteVarint32(strlen(strB));
322       coded_output->WriteRaw(strB, strlen(strB));
323       delete coded_output;  // flush
324       int64 size = pos + output->ByteCount();
325       delete output;
326 
327       ArrayInputStream* input =
328           new ArrayInputStream(buffer, size, kBlockSizes[j]);
329       CodedInputStream* coded_input = new CodedInputStream(input);
330       uint32 insize;
331       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
332       EXPECT_EQ(strlen(strA), insize);
333       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
334       EXPECT_EQ(0, memcmp(temp_buffer, strA, insize));
335 
336       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
337       EXPECT_EQ(strlen(strB), insize);
338       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
339       EXPECT_EQ(0, memcmp(temp_buffer, strB, insize));
340 
341       delete coded_input;
342       delete input;
343     }
344   }
345 
346   delete[] temp_buffer;
347   delete[] buffer;
348 }
349 
350 #if HAVE_ZLIB
TEST_F(IoTest,GzipIo)351 TEST_F(IoTest, GzipIo) {
352   const int kBufferSize = 2 * 1024;
353   uint8* buffer = new uint8[kBufferSize];
354   for (int i = 0; i < kBlockSizeCount; i++) {
355     for (int j = 0; j < kBlockSizeCount; j++) {
356       for (int z = 0; z < kBlockSizeCount; z++) {
357         int gzip_buffer_size = kBlockSizes[z];
358         int size;
359         {
360           ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
361           GzipOutputStream::Options options;
362           options.format = GzipOutputStream::GZIP;
363           if (gzip_buffer_size != -1) {
364             options.buffer_size = gzip_buffer_size;
365           }
366           GzipOutputStream gzout(&output, options);
367           WriteStuff(&gzout);
368           gzout.Close();
369           size = output.ByteCount();
370         }
371         {
372           ArrayInputStream input(buffer, size, kBlockSizes[j]);
373           GzipInputStream gzin(&input, GzipInputStream::GZIP, gzip_buffer_size);
374           ReadStuff(&gzin);
375         }
376       }
377     }
378   }
379   delete[] buffer;
380 }
381 
TEST_F(IoTest,GzipIoWithFlush)382 TEST_F(IoTest, GzipIoWithFlush) {
383   const int kBufferSize = 2 * 1024;
384   uint8* buffer = new uint8[kBufferSize];
385   // We start with i = 4 as we want a block size > 6. With block size <= 6
386   // Flush() fills up the entire 2K buffer with flush markers and the test
387   // fails. See documentation for Flush() for more detail.
388   for (int i = 4; i < kBlockSizeCount; i++) {
389     for (int j = 0; j < kBlockSizeCount; j++) {
390       for (int z = 0; z < kBlockSizeCount; z++) {
391         int gzip_buffer_size = kBlockSizes[z];
392         int size;
393         {
394           ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
395           GzipOutputStream::Options options;
396           options.format = GzipOutputStream::GZIP;
397           if (gzip_buffer_size != -1) {
398             options.buffer_size = gzip_buffer_size;
399           }
400           GzipOutputStream gzout(&output, options);
401           WriteStuff(&gzout);
402           EXPECT_TRUE(gzout.Flush());
403           gzout.Close();
404           size = output.ByteCount();
405         }
406         {
407           ArrayInputStream input(buffer, size, kBlockSizes[j]);
408           GzipInputStream gzin(&input, GzipInputStream::GZIP, gzip_buffer_size);
409           ReadStuff(&gzin);
410         }
411       }
412     }
413   }
414   delete[] buffer;
415 }
416 
TEST_F(IoTest,GzipIoContiguousFlushes)417 TEST_F(IoTest, GzipIoContiguousFlushes) {
418   const int kBufferSize = 2 * 1024;
419   uint8* buffer = new uint8[kBufferSize];
420 
421   int block_size = kBlockSizes[4];
422   int gzip_buffer_size = block_size;
423   int size;
424 
425   ArrayOutputStream output(buffer, kBufferSize, block_size);
426   GzipOutputStream::Options options;
427   options.format = GzipOutputStream::GZIP;
428   if (gzip_buffer_size != -1) {
429     options.buffer_size = gzip_buffer_size;
430   }
431   GzipOutputStream gzout(&output, options);
432   WriteStuff(&gzout);
433   EXPECT_TRUE(gzout.Flush());
434   EXPECT_TRUE(gzout.Flush());
435   gzout.Close();
436   size = output.ByteCount();
437 
438   ArrayInputStream input(buffer, size, block_size);
439   GzipInputStream gzin(&input, GzipInputStream::GZIP, gzip_buffer_size);
440   ReadStuff(&gzin);
441 
442   delete[] buffer;
443 }
444 
TEST_F(IoTest,GzipIoReadAfterFlush)445 TEST_F(IoTest, GzipIoReadAfterFlush) {
446   const int kBufferSize = 2 * 1024;
447   uint8* buffer = new uint8[kBufferSize];
448 
449   int block_size = kBlockSizes[4];
450   int gzip_buffer_size = block_size;
451   int size;
452   ArrayOutputStream output(buffer, kBufferSize, block_size);
453   GzipOutputStream::Options options;
454   options.format = GzipOutputStream::GZIP;
455   if (gzip_buffer_size != -1) {
456     options.buffer_size = gzip_buffer_size;
457   }
458 
459   GzipOutputStream gzout(&output, options);
460   WriteStuff(&gzout);
461   EXPECT_TRUE(gzout.Flush());
462   size = output.ByteCount();
463 
464   ArrayInputStream input(buffer, size, block_size);
465   GzipInputStream gzin(&input, GzipInputStream::GZIP, gzip_buffer_size);
466   ReadStuff(&gzin);
467 
468   gzout.Close();
469 
470   delete[] buffer;
471 }
472 
TEST_F(IoTest,ZlibIo)473 TEST_F(IoTest, ZlibIo) {
474   const int kBufferSize = 2 * 1024;
475   uint8* buffer = new uint8[kBufferSize];
476   for (int i = 0; i < kBlockSizeCount; i++) {
477     for (int j = 0; j < kBlockSizeCount; j++) {
478       for (int z = 0; z < kBlockSizeCount; z++) {
479         int gzip_buffer_size = kBlockSizes[z];
480         int size;
481         {
482           ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]);
483           GzipOutputStream::Options options;
484           options.format = GzipOutputStream::ZLIB;
485           if (gzip_buffer_size != -1) {
486             options.buffer_size = gzip_buffer_size;
487           }
488           GzipOutputStream gzout(&output, options);
489           WriteStuff(&gzout);
490           gzout.Close();
491           size = output.ByteCount();
492         }
493         {
494           ArrayInputStream input(buffer, size, kBlockSizes[j]);
495           GzipInputStream gzin(&input, GzipInputStream::ZLIB, gzip_buffer_size);
496           ReadStuff(&gzin);
497         }
498       }
499     }
500   }
501   delete[] buffer;
502 }
503 
TEST_F(IoTest,ZlibIoInputAutodetect)504 TEST_F(IoTest, ZlibIoInputAutodetect) {
505   const int kBufferSize = 2 * 1024;
506   uint8* buffer = new uint8[kBufferSize];
507   int size;
508   {
509     ArrayOutputStream output(buffer, kBufferSize);
510     GzipOutputStream::Options options;
511     options.format = GzipOutputStream::ZLIB;
512     GzipOutputStream gzout(&output, options);
513     WriteStuff(&gzout);
514     gzout.Close();
515     size = output.ByteCount();
516   }
517   {
518     ArrayInputStream input(buffer, size);
519     GzipInputStream gzin(&input, GzipInputStream::AUTO);
520     ReadStuff(&gzin);
521   }
522   {
523     ArrayOutputStream output(buffer, kBufferSize);
524     GzipOutputStream::Options options;
525     options.format = GzipOutputStream::GZIP;
526     GzipOutputStream gzout(&output, options);
527     WriteStuff(&gzout);
528     gzout.Close();
529     size = output.ByteCount();
530   }
531   {
532     ArrayInputStream input(buffer, size);
533     GzipInputStream gzin(&input, GzipInputStream::AUTO);
534     ReadStuff(&gzin);
535   }
536   delete[] buffer;
537 }
538 
Compress(const std::string & data,const GzipOutputStream::Options & options)539 std::string IoTest::Compress(const std::string& data,
540                              const GzipOutputStream::Options& options) {
541   std::string result;
542   {
543     StringOutputStream output(&result);
544     GzipOutputStream gzout(&output, options);
545     WriteToOutput(&gzout, data.data(), data.size());
546   }
547   return result;
548 }
549 
Uncompress(const std::string & data)550 std::string IoTest::Uncompress(const std::string& data) {
551   std::string result;
552   {
553     ArrayInputStream input(data.data(), data.size());
554     GzipInputStream gzin(&input);
555     const void* buffer;
556     int size;
557     while (gzin.Next(&buffer, &size)) {
558       result.append(reinterpret_cast<const char*>(buffer), size);
559     }
560   }
561   return result;
562 }
563 
TEST_F(IoTest,CompressionOptions)564 TEST_F(IoTest, CompressionOptions) {
565   // Some ad-hoc testing of compression options.
566 
567   std::string golden_filename =
568       TestUtil::GetTestDataPath("net/proto2/internal/testdata/golden_message");
569   std::string golden;
570   GOOGLE_CHECK_OK(File::GetContents(golden_filename, &golden, true));
571 
572   GzipOutputStream::Options options;
573   std::string gzip_compressed = Compress(golden, options);
574 
575   options.compression_level = 0;
576   std::string not_compressed = Compress(golden, options);
577 
578   // Try zlib compression for fun.
579   options = GzipOutputStream::Options();
580   options.format = GzipOutputStream::ZLIB;
581   std::string zlib_compressed = Compress(golden, options);
582 
583   // Uncompressed should be bigger than the original since it should have some
584   // sort of header.
585   EXPECT_GT(not_compressed.size(), golden.size());
586 
587   // Higher compression levels should result in smaller sizes.
588   EXPECT_LT(zlib_compressed.size(), not_compressed.size());
589 
590   // ZLIB format should differ from GZIP format.
591   EXPECT_TRUE(zlib_compressed != gzip_compressed);
592 
593   // Everything should decompress correctly.
594   EXPECT_TRUE(Uncompress(not_compressed) == golden);
595   EXPECT_TRUE(Uncompress(gzip_compressed) == golden);
596   EXPECT_TRUE(Uncompress(zlib_compressed) == golden);
597 }
598 
TEST_F(IoTest,TwoSessionWriteGzip)599 TEST_F(IoTest, TwoSessionWriteGzip) {
600   // Test that two concatenated gzip streams can be read correctly
601 
602   static const char* strA = "0123456789";
603   static const char* strB = "QuickBrownFox";
604   const int kBufferSize = 2 * 1024;
605   uint8* buffer = new uint8[kBufferSize];
606   char* temp_buffer = new char[40];
607 
608   for (int i = 0; i < kBlockSizeCount; i++) {
609     for (int j = 0; j < kBlockSizeCount; j++) {
610       ArrayOutputStream* output =
611           new ArrayOutputStream(buffer, kBufferSize, kBlockSizes[i]);
612       GzipOutputStream* gzout = new GzipOutputStream(output);
613       CodedOutputStream* coded_output = new CodedOutputStream(gzout);
614       int32 outlen = strlen(strA) + 1;
615       coded_output->WriteVarint32(outlen);
616       coded_output->WriteRaw(strA, outlen);
617       delete coded_output;  // flush
618       delete gzout;         // flush
619       int64 pos = output->ByteCount();
620       delete output;
621       output = new ArrayOutputStream(buffer + pos, kBufferSize - pos,
622                                      kBlockSizes[i]);
623       gzout = new GzipOutputStream(output);
624       coded_output = new CodedOutputStream(gzout);
625       outlen = strlen(strB) + 1;
626       coded_output->WriteVarint32(outlen);
627       coded_output->WriteRaw(strB, outlen);
628       delete coded_output;  // flush
629       delete gzout;         // flush
630       int64 size = pos + output->ByteCount();
631       delete output;
632 
633       ArrayInputStream* input =
634           new ArrayInputStream(buffer, size, kBlockSizes[j]);
635       GzipInputStream* gzin = new GzipInputStream(input);
636       CodedInputStream* coded_input = new CodedInputStream(gzin);
637       uint32 insize;
638       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
639       EXPECT_EQ(strlen(strA) + 1, insize);
640       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
641       EXPECT_EQ(0, memcmp(temp_buffer, strA, insize))
642           << "strA=" << strA << " in=" << temp_buffer;
643 
644       EXPECT_TRUE(coded_input->ReadVarint32(&insize));
645       EXPECT_EQ(strlen(strB) + 1, insize);
646       EXPECT_TRUE(coded_input->ReadRaw(temp_buffer, insize));
647       EXPECT_EQ(0, memcmp(temp_buffer, strB, insize))
648           << " out_block_size=" << kBlockSizes[i]
649           << " in_block_size=" << kBlockSizes[j] << " pos=" << pos
650           << " size=" << size << " strB=" << strB << " in=" << temp_buffer;
651 
652       delete coded_input;
653       delete gzin;
654       delete input;
655     }
656   }
657 
658   delete[] temp_buffer;
659   delete[] buffer;
660 }
661 
TEST_F(IoTest,GzipInputByteCountAfterClosed)662 TEST_F(IoTest, GzipInputByteCountAfterClosed) {
663   std::string golden = "abcdefghijklmnopqrstuvwxyz";
664   std::string compressed = Compress(golden, GzipOutputStream::Options());
665 
666   for (int i = 0; i < kBlockSizeCount; i++) {
667     ArrayInputStream arr_input(compressed.data(), compressed.size(),
668                                kBlockSizes[i]);
669     GzipInputStream gz_input(&arr_input);
670     const void* buffer;
671     int size;
672     while (gz_input.Next(&buffer, &size)) {
673       EXPECT_LE(gz_input.ByteCount(), golden.size());
674     }
675     EXPECT_EQ(golden.size(), gz_input.ByteCount());
676   }
677 }
678 
TEST_F(IoTest,GzipInputByteCountAfterClosedConcatenatedStreams)679 TEST_F(IoTest, GzipInputByteCountAfterClosedConcatenatedStreams) {
680   std::string golden1 = "abcdefghijklmnopqrstuvwxyz";
681   std::string golden2 = "the quick brown fox jumps over the lazy dog";
682   const size_t total_size = golden1.size() + golden2.size();
683   std::string compressed = Compress(golden1, GzipOutputStream::Options()) +
684                            Compress(golden2, GzipOutputStream::Options());
685 
686   for (int i = 0; i < kBlockSizeCount; i++) {
687     ArrayInputStream arr_input(compressed.data(), compressed.size(),
688                                kBlockSizes[i]);
689     GzipInputStream gz_input(&arr_input);
690     const void* buffer;
691     int size;
692     while (gz_input.Next(&buffer, &size)) {
693       EXPECT_LE(gz_input.ByteCount(), total_size);
694     }
695     EXPECT_EQ(total_size, gz_input.ByteCount());
696   }
697 }
698 #endif
699 
700 // There is no string input, only string output.  Also, it doesn't support
701 // explicit block sizes.  So, we'll only run one test and we'll use
702 // ArrayInput to read back the results.
TEST_F(IoTest,StringIo)703 TEST_F(IoTest, StringIo) {
704   std::string str;
705   {
706     StringOutputStream output(&str);
707     WriteStuff(&output);
708   }
709   {
710     ArrayInputStream input(str.data(), str.size());
711     ReadStuff(&input);
712   }
713 }
714 
715 // Verifies that outputs up to kint32max can be created.
TEST_F(IoTest,LargeOutput)716 TEST_F(IoTest, LargeOutput) {
717   std::string str;
718   StringOutputStream output(&str);
719   void* unused_data;
720   int size;
721   // Repeatedly calling Next should eventually grow the buffer to kint32max.
722   do {
723     EXPECT_TRUE(output.Next(&unused_data, &size));
724   } while (str.size() < std::numeric_limits<int>::max());
725   // Further increases should be possible.
726   output.Next(&unused_data, &size);
727   EXPECT_GT(size, 0);
728 }
729 
730 
731 // To test files, we create a temporary file, write, read, truncate, repeat.
TEST_F(IoTest,FileIo)732 TEST_F(IoTest, FileIo) {
733   std::string filename = TestTempDir() + "/zero_copy_stream_test_file";
734 
735   for (int i = 0; i < kBlockSizeCount; i++) {
736     for (int j = 0; j < kBlockSizeCount; j++) {
737       // Make a temporary file.
738       int file =
739           open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
740       ASSERT_GE(file, 0);
741 
742       {
743         FileOutputStream output(file, kBlockSizes[i]);
744         WriteStuff(&output);
745         EXPECT_EQ(0, output.GetErrno());
746       }
747 
748       // Rewind.
749       ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
750 
751       {
752         FileInputStream input(file, kBlockSizes[j]);
753         ReadStuff(&input);
754         EXPECT_EQ(0, input.GetErrno());
755       }
756 
757       close(file);
758     }
759   }
760 }
761 
762 #if HAVE_ZLIB
TEST_F(IoTest,GzipFileIo)763 TEST_F(IoTest, GzipFileIo) {
764   std::string filename = TestTempDir() + "/zero_copy_stream_test_file";
765 
766   for (int i = 0; i < kBlockSizeCount; i++) {
767     for (int j = 0; j < kBlockSizeCount; j++) {
768       // Make a temporary file.
769       int file =
770           open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777);
771       ASSERT_GE(file, 0);
772       {
773         FileOutputStream output(file, kBlockSizes[i]);
774         GzipOutputStream gzout(&output);
775         WriteStuffLarge(&gzout);
776         gzout.Close();
777         output.Flush();
778         EXPECT_EQ(0, output.GetErrno());
779       }
780 
781       // Rewind.
782       ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1);
783 
784       {
785         FileInputStream input(file, kBlockSizes[j]);
786         GzipInputStream gzin(&input);
787         ReadStuffLarge(&gzin);
788         EXPECT_EQ(0, input.GetErrno());
789       }
790 
791       close(file);
792     }
793   }
794 }
795 #endif
796 
797 // MSVC raises various debugging exceptions if we try to use a file
798 // descriptor of -1, defeating our tests below.  This class will disable
799 // these debug assertions while in scope.
800 class MsvcDebugDisabler {
801  public:
802 #if defined(_MSC_VER) && _MSC_VER >= 1400
MsvcDebugDisabler()803   MsvcDebugDisabler() {
804     old_handler_ = _set_invalid_parameter_handler(MyHandler);
805     old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0);
806   }
~MsvcDebugDisabler()807   ~MsvcDebugDisabler() {
808     old_handler_ = _set_invalid_parameter_handler(old_handler_);
809     old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_);
810   }
811 
MyHandler(const wchar_t * expr,const wchar_t * func,const wchar_t * file,unsigned int line,uintptr_t pReserved)812   static void MyHandler(const wchar_t* expr, const wchar_t* func,
813                         const wchar_t* file, unsigned int line,
814                         uintptr_t pReserved) {
815     // do nothing
816   }
817 
818   _invalid_parameter_handler old_handler_;
819   int old_mode_;
820 #else
821   // Dummy constructor and destructor to ensure that GCC doesn't complain
822   // that debug_disabler is an unused variable.
823   MsvcDebugDisabler() {}
824   ~MsvcDebugDisabler() {}
825 #endif
826 };
827 
828 // Test that FileInputStreams report errors correctly.
TEST_F(IoTest,FileReadError)829 TEST_F(IoTest, FileReadError) {
830   MsvcDebugDisabler debug_disabler;
831 
832   // -1 = invalid file descriptor.
833   FileInputStream input(-1);
834 
835   const void* buffer;
836   int size;
837   EXPECT_FALSE(input.Next(&buffer, &size));
838   EXPECT_EQ(EBADF, input.GetErrno());
839 }
840 
841 // Test that FileOutputStreams report errors correctly.
TEST_F(IoTest,FileWriteError)842 TEST_F(IoTest, FileWriteError) {
843   MsvcDebugDisabler debug_disabler;
844 
845   // -1 = invalid file descriptor.
846   FileOutputStream input(-1);
847 
848   void* buffer;
849   int size;
850 
851   // The first call to Next() succeeds because it doesn't have anything to
852   // write yet.
853   EXPECT_TRUE(input.Next(&buffer, &size));
854 
855   // Second call fails.
856   EXPECT_FALSE(input.Next(&buffer, &size));
857 
858   EXPECT_EQ(EBADF, input.GetErrno());
859 }
860 
861 // Pipes are not seekable, so File{Input,Output}Stream ends up doing some
862 // different things to handle them.  We'll test by writing to a pipe and
863 // reading back from it.
TEST_F(IoTest,PipeIo)864 TEST_F(IoTest, PipeIo) {
865   int files[2];
866 
867   for (int i = 0; i < kBlockSizeCount; i++) {
868     for (int j = 0; j < kBlockSizeCount; j++) {
869       // Need to create a new pipe each time because ReadStuff() expects
870       // to see EOF at the end.
871       ASSERT_EQ(pipe(files), 0);
872 
873       {
874         FileOutputStream output(files[1], kBlockSizes[i]);
875         WriteStuff(&output);
876         EXPECT_EQ(0, output.GetErrno());
877       }
878       close(files[1]);  // Send EOF.
879 
880       {
881         FileInputStream input(files[0], kBlockSizes[j]);
882         ReadStuff(&input);
883         EXPECT_EQ(0, input.GetErrno());
884       }
885       close(files[0]);
886     }
887   }
888 }
889 
890 // Test using C++ iostreams.
TEST_F(IoTest,IostreamIo)891 TEST_F(IoTest, IostreamIo) {
892   for (int i = 0; i < kBlockSizeCount; i++) {
893     for (int j = 0; j < kBlockSizeCount; j++) {
894       {
895         std::stringstream stream;
896 
897         {
898           OstreamOutputStream output(&stream, kBlockSizes[i]);
899           WriteStuff(&output);
900           EXPECT_FALSE(stream.fail());
901         }
902 
903         {
904           IstreamInputStream input(&stream, kBlockSizes[j]);
905           ReadStuff(&input);
906           EXPECT_TRUE(stream.eof());
907         }
908       }
909 
910       {
911         std::stringstream stream;
912 
913         {
914           OstreamOutputStream output(&stream, kBlockSizes[i]);
915           WriteStuffLarge(&output);
916           EXPECT_FALSE(stream.fail());
917         }
918 
919         {
920           IstreamInputStream input(&stream, kBlockSizes[j]);
921           ReadStuffLarge(&input);
922           EXPECT_TRUE(stream.eof());
923         }
924       }
925     }
926   }
927 }
928 
929 // To test ConcatenatingInputStream, we create several ArrayInputStreams
930 // covering a buffer and then concatenate them.
TEST_F(IoTest,ConcatenatingInputStream)931 TEST_F(IoTest, ConcatenatingInputStream) {
932   const int kBufferSize = 256;
933   uint8 buffer[kBufferSize];
934 
935   // Fill the buffer.
936   ArrayOutputStream output(buffer, kBufferSize);
937   WriteStuff(&output);
938 
939   // Now split it up into multiple streams of varying sizes.
940   ASSERT_EQ(68, output.ByteCount());  // Test depends on this.
941   ArrayInputStream input1(buffer, 12);
942   ArrayInputStream input2(buffer + 12, 7);
943   ArrayInputStream input3(buffer + 19, 6);
944   ArrayInputStream input4(buffer + 25, 15);
945   ArrayInputStream input5(buffer + 40, 0);
946   // Note:  We want to make sure we have a stream boundary somewhere between
947   // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff().  This
948   // tests that a bug that existed in the original code for Skip() is fixed.
949   ArrayInputStream input6(buffer + 40, 10);
950   ArrayInputStream input7(buffer + 50, 18);  // Total = 68 bytes.
951 
952   ZeroCopyInputStream* streams[] = {&input1, &input2, &input3, &input4,
953                                     &input5, &input6, &input7};
954 
955   // Create the concatenating stream and read.
956   ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams));
957   ReadStuff(&input);
958 }
959 
960 // To test LimitingInputStream, we write our golden text to a buffer, then
961 // create an ArrayInputStream that contains the whole buffer (not just the
962 // bytes written), then use a LimitingInputStream to limit it just to the
963 // bytes written.
TEST_F(IoTest,LimitingInputStream)964 TEST_F(IoTest, LimitingInputStream) {
965   const int kBufferSize = 256;
966   uint8 buffer[kBufferSize];
967 
968   // Fill the buffer.
969   ArrayOutputStream output(buffer, kBufferSize);
970   WriteStuff(&output);
971 
972   // Set up input.
973   ArrayInputStream array_input(buffer, kBufferSize);
974   LimitingInputStream input(&array_input, output.ByteCount());
975 
976   ReadStuff(&input);
977 }
978 
979 // Checks that ByteCount works correctly for LimitingInputStreams where the
980 // underlying stream has already been read.
TEST_F(IoTest,LimitingInputStreamByteCount)981 TEST_F(IoTest, LimitingInputStreamByteCount) {
982   const int kHalfBufferSize = 128;
983   const int kBufferSize = kHalfBufferSize * 2;
984   uint8 buffer[kBufferSize];
985 
986   // Set up input. Only allow half to be read at once.
987   ArrayInputStream array_input(buffer, kBufferSize, kHalfBufferSize);
988   const void* data;
989   int size;
990   EXPECT_TRUE(array_input.Next(&data, &size));
991   EXPECT_EQ(kHalfBufferSize, array_input.ByteCount());
992   // kHalfBufferSize - 1 to test limiting logic as well.
993   LimitingInputStream input(&array_input, kHalfBufferSize - 1);
994   EXPECT_EQ(0, input.ByteCount());
995   EXPECT_TRUE(input.Next(&data, &size));
996   EXPECT_EQ(kHalfBufferSize - 1, input.ByteCount());
997 }
998 
999 // Check that a zero-size array doesn't confuse the code.
TEST(ZeroSizeArray,Input)1000 TEST(ZeroSizeArray, Input) {
1001   ArrayInputStream input(NULL, 0);
1002   const void* data;
1003   int size;
1004   EXPECT_FALSE(input.Next(&data, &size));
1005 }
1006 
TEST(ZeroSizeArray,Output)1007 TEST(ZeroSizeArray, Output) {
1008   ArrayOutputStream output(NULL, 0);
1009   void* data;
1010   int size;
1011   EXPECT_FALSE(output.Next(&data, &size));
1012 }
1013 
1014 }  // namespace
1015 }  // namespace io
1016 }  // namespace protobuf
1017 }  // namespace google
1018