Add xz decompression method.

Use the method to check compressed data in debug builds.

Test: boot.oat is identical
Change-Id: Id796d4ba19c338522fc855f751bac00555029c79
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 71422d4..e13f8be 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -134,7 +134,7 @@
   CHECK(builder->Good());
   std::vector<uint8_t> compressed_buffer;
   compressed_buffer.reserve(buffer.size() / 4);
-  XzCompress(ArrayRef<uint8_t>(buffer), &compressed_buffer);
+  XzCompress(ArrayRef<const uint8_t>(buffer), &compressed_buffer);
   return compressed_buffer;
 }
 
diff --git a/compiler/debug/xz_utils.cc b/compiler/debug/xz_utils.cc
index a9e30a6..a8f60ac 100644
--- a/compiler/debug/xz_utils.cc
+++ b/compiler/debug/xz_utils.cc
@@ -17,13 +17,16 @@
 #include "xz_utils.h"
 
 #include <vector>
+#include <mutex>
 
 #include "base/array_ref.h"
-#include "dwarf/writer.h"
+#include "base/bit_utils.h"
 #include "base/leb128.h"
+#include "dwarf/writer.h"
 
 // liblzma.
 #include "7zCrc.h"
+#include "Xz.h"
 #include "XzCrc64.h"
 #include "XzEnc.h"
 
@@ -32,10 +35,17 @@
 
 constexpr size_t kChunkSize = kPageSize;
 
-static void XzCompressChunk(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst) {
+static void XzInitCrc() {
+  static std::once_flag crc_initialized;
+  std::call_once(crc_initialized, []() {
+    CrcGenerateTable();
+    Crc64GenerateTable();
+  });
+}
+
+static void XzCompressChunk(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
   // Configure the compression library.
-  CrcGenerateTable();
-  Crc64GenerateTable();
+  XzInitCrc();
   CLzma2EncProps lzma2Props;
   Lzma2EncProps_Init(&lzma2Props);
   lzma2Props.lzmaProps.level = 1;  // Fast compression.
@@ -62,7 +72,7 @@
       return SZ_OK;
     }
     size_t src_pos_;
-    ArrayRef<uint8_t> src_;
+    ArrayRef<const uint8_t> src_;
     std::vector<uint8_t>* dst_;
   };
   XzCallbacks callbacks;
@@ -85,7 +95,7 @@
 // In short, the file format is: [header] [compressed_block]* [index] [footer]
 // Where [index] is: [num_records] ([compressed_size] [uncompressed_size])* [crc32]
 //
-void XzCompress(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst) {
+void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
   uint8_t header[] = { 0xFD, '7', 'z', 'X', 'Z', 0, 0, 1, 0x69, 0x22, 0xDE, 0x36 };
   uint8_t footer[] = { 0, 1, 'Y', 'Z' };
   dst->insert(dst->end(), header, header + sizeof(header));
@@ -138,6 +148,47 @@
     writer.UpdateUint32(0, CrcCalc(tmp.data() + 4, 6));
     dst->insert(dst->end(), tmp.begin(), tmp.end());
   }
+
+  // Decompress the data back and check that we get the original.
+  if (kIsDebugBuild) {
+    std::vector<uint8_t> decompressed;
+    XzDecompress(ArrayRef<const uint8_t>(*dst), &decompressed);
+    DCHECK_EQ(decompressed.size(), src.size());
+    DCHECK_EQ(memcmp(decompressed.data(), src.data(), src.size()), 0);
+  }
+}
+
+void XzDecompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
+  XzInitCrc();
+  std::unique_ptr<CXzUnpacker> state(new CXzUnpacker());
+  ISzAlloc alloc;
+  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
+  XzUnpacker_Construct(state.get(), &alloc);
+
+  size_t src_offset = 0;
+  size_t dst_offset = 0;
+  ECoderStatus status;
+  do {
+    dst->resize(RoundUp(dst_offset + kPageSize / 4, kPageSize));
+    size_t src_remaining = src.size() - src_offset;
+    size_t dst_remaining = dst->size() - dst_offset;
+    int return_val = XzUnpacker_Code(state.get(),
+                                     dst->data() + dst_offset,
+                                     &dst_remaining,
+                                     src.data() + src_offset,
+                                     &src_remaining,
+                                     true,
+                                     CODER_FINISH_ANY,
+                                     &status);
+    CHECK_EQ(return_val, SZ_OK);
+    src_offset += src_remaining;
+    dst_offset += dst_remaining;
+  } while (status == CODER_STATUS_NOT_FINISHED);
+  CHECK_EQ(src_offset, src.size());
+  CHECK(XzUnpacker_IsStreamWasFinished(state.get()));
+  XzUnpacker_Free(state.get());
+  dst->resize(dst_offset);
 }
 
 }  // namespace debug
diff --git a/compiler/debug/xz_utils.h b/compiler/debug/xz_utils.h
index c4076c6..731b03c 100644
--- a/compiler/debug/xz_utils.h
+++ b/compiler/debug/xz_utils.h
@@ -24,7 +24,8 @@
 namespace art {
 namespace debug {
 
-void XzCompress(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst);
+void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst);
+void XzDecompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst);
 
 }  // namespace debug
 }  // namespace art