AU: Extent writer utility classes

These are similar to the FileWriter classes, but focus on writing to
extents within a file (or device) rather than writing a file in order.

Again there is a ExtentWriter interface from which all types of extent
writers interit.

There are also two basic extent writers: a direct extent writer that
writes data directly to the file descriptor, and a zero padding extent
writer that piggy-backs on another extent writer. When the
zero-padding extent writer is End()ed, it fills out the rest of the
extent with zeros.

Also, BzipExtentWriter: an ExtentWriter concrete subclass that writes
to another ExtentWriter.  BzipExtentWriter bzip2 decompresses all data
passed through.

Review URL: http://codereview.chromium.org/551132
diff --git a/bzip_extent_writer.cc b/bzip_extent_writer.cc
new file mode 100644
index 0000000..cdf0ce3
--- /dev/null
+++ b/bzip_extent_writer.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/bzip_extent_writer.h"
+
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+const vector<char>::size_type kOutputBufferLength = 1024 * 1024;
+}
+
+bool BzipExtentWriter::Init(int fd,
+                            const vector<Extent>& extents,
+                            uint32 block_size) {
+  // Init bzip2 stream
+  int rc = BZ2_bzDecompressInit(&stream_,
+                                0,  // verbosity. (0 == silent)
+                                0  // 0 = faster algo, more memory
+                                );
+  TEST_AND_RETURN_FALSE(rc == BZ_OK);
+
+  return next_->Init(fd, extents, block_size);
+}
+
+bool BzipExtentWriter::Write(const void* bytes, size_t count) {
+  vector<char> output_buffer(kOutputBufferLength);
+
+  const char* c_bytes = reinterpret_cast<const char*>(bytes);
+
+  input_buffer_.insert(input_buffer_.end(), c_bytes, c_bytes + count);
+  
+  stream_.next_in = &input_buffer_[0];
+  stream_.avail_in = input_buffer_.size();
+  
+  for (;;) {
+    stream_.next_out = &output_buffer[0];
+    stream_.avail_out = output_buffer.size();
+
+    int rc = BZ2_bzDecompress(&stream_);
+    TEST_AND_RETURN_FALSE(rc == BZ_OK || rc == BZ_STREAM_END);
+    
+    if (stream_.avail_out == output_buffer.size())
+      break;  // got no new bytes
+    
+    TEST_AND_RETURN_FALSE(
+        next_->Write(&output_buffer[0],
+                     output_buffer.size() - stream_.avail_out));
+    
+    if (rc == BZ_STREAM_END)
+      CHECK_EQ(stream_.avail_in, 0);
+    if (stream_.avail_in == 0)
+      break;  // no more input to process
+  }
+
+  // store unconsumed data in input_buffer_.
+  
+  vector<char> new_input_buffer(input_buffer_.end() - stream_.avail_in,
+                                input_buffer_.end());
+  new_input_buffer.swap(input_buffer_);
+  
+  return true;
+}
+
+bool BzipExtentWriter::EndImpl() {
+  TEST_AND_RETURN_FALSE(input_buffer_.empty());
+  TEST_AND_RETURN_FALSE(BZ2_bzDecompressEnd(&stream_) == BZ_OK);
+  return next_->End();
+}
+
+}  // namespace chromeos_update_engine