Allow raw dex files to be read from arrays of base64 encoded bytes.

This allows tests to be run without opening files on local disk.

Change-Id: I05296bfba584d76ae272d4905b5c45ae10687bc6
diff --git a/Android.common.mk b/Android.common.mk
index 5bb20fa..9f7fa43 100644
--- a/Android.common.mk
+++ b/Android.common.mk
@@ -41,6 +41,7 @@
 
 LIBART_LOCAL_SRC_FILES := \
 	src/assembler.cc \
+	src/base64.cc \
 	src/dex_file.cc \
 	src/dex_instruction.cc \
 	src/memory_region.cc \
diff --git a/src/base64.cc b/src/base64.cc
new file mode 100644
index 0000000..e745a62
--- /dev/null
+++ b/src/base64.cc
@@ -0,0 +1,76 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "src/globals.h"
+#include "src/scoped_ptr.h"
+
+#include <vector>
+
+namespace art {
+
+static const byte kMap[256] = {
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
+  52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
+  255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
+    7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
+   19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
+  255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
+   37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
+   49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255
+};
+
+byte* DecodeBase64(const char* src, size_t size, size_t* dst_size) {
+  std::vector<byte> tmp;
+  unsigned long t = 0, y = 0;
+  int g = 3;
+  for (size_t i = 0; i < size; ++i) {
+    byte c = kMap[src[i] & 0xFF];
+    if (c == 255) continue;
+    // the final = symbols are read and used to trim the remaining bytes
+    if (c == 254) {
+      c = 0;
+      // prevent g < 0 which would potentially allow an overflow later
+      if (--g < 0) {
+        return NULL;
+      }
+    } else if (g != 3) {
+      // we only allow = to be at the end
+      return NULL;
+    }
+    t = (t << 6) | c;
+    if (++y == 4) {
+      tmp.push_back((t >> 16) & 255);
+      if (g > 1) {
+        tmp.push_back((t >> 8) & 255);
+      }
+      if (g > 2) {
+        tmp.push_back(t & 255);
+      }
+      y = t = 0;
+    }
+  }
+  if (y != 0) {
+    return NULL;
+  }
+  scoped_ptr<byte> dst(new byte[tmp.size()]);
+  if (dst_size != NULL) {
+    *dst_size = tmp.size();
+  }
+  std::copy(tmp.begin(), tmp.end(), dst.get());
+  return dst.release();
+}
+
+};
diff --git a/src/base64.h b/src/base64.h
new file mode 100644
index 0000000..320148d
--- /dev/null
+++ b/src/base64.h
@@ -0,0 +1,14 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_BASE64_H_
+#define ART_SRC_BASE64_H_
+
+#include "src/globals.h"
+
+namespace art {
+
+byte* DecodeBase64(const char* src, size_t size, size_t* dst_size);
+
+}  // namespace art
+
+#endif  // ART_SRC_BASE64_H_
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 1951af5..1331ac7 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -9,8 +9,17 @@
 
 namespace art {
 
-DexFile* DexFile::Open(const char* filename) {
-  RawDexFile* raw = RawDexFile::Open(filename);
+DexFile* DexFile::OpenFile(const char* filename) {
+  RawDexFile* raw = RawDexFile::OpenFile(filename);
+  return Open(raw);
+}
+
+DexFile* DexFile::OpenBase64(const char* base64) {
+  RawDexFile* raw = RawDexFile::OpenBase64(base64);
+  return Open(raw);
+}
+
+DexFile* DexFile::Open(RawDexFile* raw) {
   if (raw == NULL) {
     return NULL;
   }
diff --git a/src/dex_file.h b/src/dex_file.h
index 94ba33d..51c0a5a 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -12,8 +12,16 @@
 
 class DexFile {
  public:
-  // Opens a dex file.  Returns NULL on failure.
-  static DexFile* Open(const char* filename);
+  // Opens a .dex file from the file system.  Returns NULL on failure.
+  static DexFile* OpenFile(const char* filename);
+
+  // Opens a .dex file from a base64 encoded array.  Returns NULL on
+  // failure.
+  static DexFile* OpenBase64(const char* base64);
+
+  // Opens a .dex file from a RawDexFile.  Takes ownership of the
+  // RawDexFile.
+  static DexFile* Open(RawDexFile* raw);
 
   // Close and deallocate.
   ~DexFile();
diff --git a/src/dex_file_test.cc b/src/dex_file_test.cc
index 74adca3..b46cbb5 100644
--- a/src/dex_file_test.cc
+++ b/src/dex_file_test.cc
@@ -8,16 +8,37 @@
 
 namespace art {
 
-static const char* filename =
-    "/usr/local/google/work/dalvik-dev-git/Nested.dex";
+// Nested.java
+//
+// class Nested {
+//     class Inner {
+//     }
+// }
+static const char kNestedDex[] =
+  "ZGV4CjAzNQAQedgAe7gM1B/WHsWJ6L7lGAISGC7yjD2IAwAAcAAAAHhWNBIAAAAAAAAAAMQCAAAP"
+  "AAAAcAAAAAcAAACsAAAAAgAAAMgAAAABAAAA4AAAAAMAAADoAAAAAgAAAAABAABIAgAAQAEAAK4B"
+  "AAC2AQAAvQEAAM0BAADXAQAA+wEAABsCAAA+AgAAUgIAAF8CAABiAgAAZgIAAHMCAAB5AgAAgQIA"
+  "AAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABgAAAAAAAAAKAAAABgAAAKgBAAAAAAEA"
+  "DQAAAAAAAQAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAIAAAAiAEAAKsCAAAA"
+  "AAAAAQAAAAAAAAAFAAAAAAAAAAgAAACYAQAAuAIAAAAAAAACAAAAlAIAAJoCAAABAAAAowIAAAIA"
+  "AgABAAAAiAIAAAYAAABbAQAAcBACAAAADgABAAEAAQAAAI4CAAAEAAAAcBACAAAADgBAAQAAAAAA"
+  "AAAAAAAAAAAATAEAAAAAAAAAAAAAAAAAAAEAAAABAAY8aW5pdD4ABUlubmVyAA5MTmVzdGVkJElu"
+  "bmVyOwAITE5lc3RlZDsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2"
+  "aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNz"
+  "ZXM7ABJMamF2YS9sYW5nL09iamVjdDsAC05lc3RlZC5qYXZhAAFWAAJWTAALYWNjZXNzRmxhZ3MA"
+  "BG5hbWUABnRoaXMkMAAFdmFsdWUAAgEABw4AAQAHDjwAAgIBDhgBAgMCCwQADBcBAgQBDhwBGAAA"
+  "AQEAAJAgAICABNQCAAABAAGAgATwAgAAEAAAAAAAAAABAAAAAAAAAAEAAAAPAAAAcAAAAAIAAAAH"
+  "AAAArAAAAAMAAAACAAAAyAAAAAQAAAABAAAA4AAAAAUAAAADAAAA6AAAAAYAAAACAAAAAAEAAAMQ"
+  "AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA"
+  "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
 
 TEST(DexFile, Open) {
-  scoped_ptr<DexFile> dex(DexFile::Open(filename));
+  scoped_ptr<DexFile> dex(DexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(dex != NULL);
 }
 
 TEST(DexFile, LoadNonexistent) {
-  scoped_ptr<DexFile> dex(DexFile::Open(filename));
+  scoped_ptr<DexFile> dex(DexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(dex != NULL);
 
   Class* klass = dex->LoadClass("NoSuchClass");
@@ -25,7 +46,7 @@
 }
 
 TEST(DexFile, Load) {
-  scoped_ptr<DexFile> dex(DexFile::Open(filename));
+  scoped_ptr<DexFile> dex(DexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(dex != NULL);
 
   Class* klass = dex->LoadClass("LNested;");
diff --git a/src/raw_dex_file.cc b/src/raw_dex_file.cc
index 9f09177..6f1703f 100644
--- a/src/raw_dex_file.cc
+++ b/src/raw_dex_file.cc
@@ -1,8 +1,10 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
-#include "src/raw_dex_file.h"
+#include "src/base64.h"
 #include "src/globals.h"
 #include "src/logging.h"
+#include "src/raw_dex_file.h"
+#include "src/scoped_ptr.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -17,7 +19,33 @@
 const byte RawDexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
 const byte RawDexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' };
 
-RawDexFile* RawDexFile::Open(const char* filename) {
+// Helper class to deallocate mmap-backed .dex files.
+class MmapCloser : public RawDexFile::Closer {
+ public:
+  MmapCloser(void* addr, size_t length) : addr_(addr), length_(length) {};
+  virtual ~MmapCloser() {
+    CHECK(addr_ != NULL);
+    if (munmap(addr_, length_) == -1) {
+      LG << "munmap: " << strerror(errno);  // TODO: PLOG
+    }
+  }
+ private:
+  void* addr_;
+  size_t length_;
+};
+
+// Helper class for deallocating new/delete-backed .dex files.
+class PtrCloser : public RawDexFile::Closer {
+ public:
+  PtrCloser(byte* addr) : addr_(addr) {};
+  virtual ~PtrCloser() { delete[] addr_; }
+ private:
+  byte* addr_;
+};
+
+RawDexFile::Closer::~Closer() {}
+
+RawDexFile* RawDexFile::OpenFile(const char* filename) {
   CHECK(filename != NULL);
   int fd = open(filename, O_RDONLY);
   if (fd == -1) {
@@ -39,19 +67,33 @@
     return NULL;
   }
   close(fd);
-  RawDexFile* raw = new RawDexFile(reinterpret_cast<byte*>(addr), length);
-  raw->Init();
-  return raw;
+  byte* dex_file = reinterpret_cast<byte*>(addr);
+  Closer* closer = new MmapCloser(addr, length);
+  return Open(dex_file, closer);
 }
 
-RawDexFile::~RawDexFile() {
-  CHECK(base_ != NULL);
-  const void* addr = reinterpret_cast<const void*>(base_);
-  if (munmap(const_cast<void*>(addr), length_) == -1) {
-    LG << "munmap: " << strerror(errno);  // TODO: PLOG
+RawDexFile* RawDexFile::OpenBase64(const char* base64) {
+  CHECK(base64 != NULL);
+  size_t size = strlen(base64);
+  byte* dex_file = DecodeBase64(base64, size, NULL);
+  if (dex_file == NULL) {
+    return NULL;
+  }
+  RawDexFile::Closer* closer = new PtrCloser(dex_file);
+  return Open(dex_file, closer);
+}
+
+RawDexFile* RawDexFile::Open(const byte* dex_file, Closer* closer) {
+  scoped_ptr<RawDexFile> raw(new RawDexFile(dex_file, closer));
+  if (!raw->Init()) {
+    return NULL;
+  } else {
+    return raw.release();
   }
 }
 
+RawDexFile::~RawDexFile() {}
+
 bool RawDexFile::Init() {
   InitMembers();
   if (!IsMagicValid()) {
@@ -74,7 +116,7 @@
 }
 
 bool RawDexFile::IsMagicValid() {
-  return CheckMagic(header_->magic);
+  return CheckMagic(header_->magic_);
 }
 
 bool RawDexFile::CheckMagic(const byte* magic) {
diff --git a/src/raw_dex_file.h b/src/raw_dex_file.h
index ca01486..ea2ac4d 100644
--- a/src/raw_dex_file.h
+++ b/src/raw_dex_file.h
@@ -6,6 +6,7 @@
 #include "src/globals.h"
 #include "src/leb128.h"
 #include "src/logging.h"
+#include "src/scoped_ptr.h"
 #include "src/strutil.h"
 
 #include <map>
@@ -20,9 +21,9 @@
 
   // Raw header_item.
   struct Header {
-    uint8_t magic[8];
-    uint32_t checksum;
-    uint8_t signature[kSha1DigestSize];
+    uint8_t magic_[8];
+    uint32_t checksum_;
+    uint8_t signature_[kSha1DigestSize];
     uint32_t file_size_;  // length of entire file
     uint32_t header_size_;  // offset to start of next section
     uint32_t endian_tag_;
@@ -142,11 +143,23 @@
     uint32_t code_off_;
   };
 
-  // Opens a .dex file.
-  static RawDexFile* Open(const char* filename);
+  // Helper class to deallocate underlying storage.
+  class Closer {
+   public:
+    virtual ~Closer();
+  };
+
+  // Opens a .dex file from the file system.
+  static RawDexFile* OpenFile(const char* filename);
+
+  // Opens a .dex file from a base64 encoded array.
+  static RawDexFile* OpenBase64(const char* base64);
+
+  // Opens a .dex file at a the given address.
+  static RawDexFile* Open(const byte* dex_file, Closer* closer);
 
   // Closes a .dex file.
-  ~RawDexFile();
+  virtual ~RawDexFile();
 
   const Header& GetHeader() {
     CHECK(header_ != NULL);
@@ -335,9 +348,9 @@
   }
 
  private:
-  RawDexFile(const byte* addr, size_t length)
+  RawDexFile(const byte* addr, Closer* closer)
       : base_(addr),
-        length_(length),
+        closer_(closer),
         header_(0),
         string_ids_(0),
         type_ids_(0),
@@ -368,8 +381,8 @@
   // The base address of the memory mapping.
   const byte* base_;
 
-  // The length of the memory mapping in bytes.
-  const size_t length_;
+  // Helper object to free the underlying allocation.
+  scoped_ptr<Closer> closer_;
 
   // Points to the header section.
   const Header* header_;
diff --git a/src/raw_dex_file_test.cc b/src/raw_dex_file_test.cc
index 6347c44..6bfd82a 100644
--- a/src/raw_dex_file_test.cc
+++ b/src/raw_dex_file_test.cc
@@ -1,23 +1,73 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
 #include "src/raw_dex_file.h"
+#include "src/scoped_ptr.h"
 
 #include <stdio.h>
 #include "gtest/gtest.h"
 
 namespace art {
 
-static const char* filename =
-    "/usr/local/google/work/dalvik-dev-git/Nested.dex";
+// Nested.java
+//
+// class Nested {
+//     class Inner {
+//     }
+// }
+static const char kNestedDex[] =
+  "ZGV4CjAzNQAQedgAe7gM1B/WHsWJ6L7lGAISGC7yjD2IAwAAcAAAAHhWNBIAAAAAAAAAAMQCAAAP"
+  "AAAAcAAAAAcAAACsAAAAAgAAAMgAAAABAAAA4AAAAAMAAADoAAAAAgAAAAABAABIAgAAQAEAAK4B"
+  "AAC2AQAAvQEAAM0BAADXAQAA+wEAABsCAAA+AgAAUgIAAF8CAABiAgAAZgIAAHMCAAB5AgAAgQIA"
+  "AAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABgAAAAAAAAAKAAAABgAAAKgBAAAAAAEA"
+  "DQAAAAAAAQAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAIAAAAiAEAAKsCAAAA"
+  "AAAAAQAAAAAAAAAFAAAAAAAAAAgAAACYAQAAuAIAAAAAAAACAAAAlAIAAJoCAAABAAAAowIAAAIA"
+  "AgABAAAAiAIAAAYAAABbAQAAcBACAAAADgABAAEAAQAAAI4CAAAEAAAAcBACAAAADgBAAQAAAAAA"
+  "AAAAAAAAAAAATAEAAAAAAAAAAAAAAAAAAAEAAAABAAY8aW5pdD4ABUlubmVyAA5MTmVzdGVkJElu"
+  "bmVyOwAITE5lc3RlZDsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2"
+  "aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNz"
+  "ZXM7ABJMamF2YS9sYW5nL09iamVjdDsAC05lc3RlZC5qYXZhAAFWAAJWTAALYWNjZXNzRmxhZ3MA"
+  "BG5hbWUABnRoaXMkMAAFdmFsdWUAAgEABw4AAQAHDjwAAgIBDhgBAgMCCwQADBcBAgQBDhwBGAAA"
+  "AQEAAJAgAICABNQCAAABAAGAgATwAgAAEAAAAAAAAAABAAAAAAAAAAEAAAAPAAAAcAAAAAIAAAAH"
+  "AAAArAAAAAMAAAACAAAAyAAAAAQAAAABAAAA4AAAAAUAAAADAAAA6AAAAAYAAAACAAAAAAEAAAMQ"
+  "AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA"
+  "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
+
 
 TEST(RawDexFile, Open) {
-  RawDexFile* raw = RawDexFile::Open(filename);
+  scoped_ptr<RawDexFile> raw(RawDexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(raw != NULL);
-  delete raw;
+}
+
+TEST(RawDexFile, Header) {
+  scoped_ptr<RawDexFile> raw(RawDexFile::OpenBase64(kNestedDex));
+  ASSERT_TRUE(raw != NULL);
+
+  const RawDexFile::Header& header = raw->GetHeader();
+  // TODO: header.magic_
+  EXPECT_EQ(0x00d87910U, header.checksum_);
+  // TODO: header.signature_
+  EXPECT_EQ(904U, header.file_size_);
+  EXPECT_EQ(112U, header.header_size_);
+  EXPECT_EQ(0U, header.link_size_);
+  EXPECT_EQ(0U, header.link_off_);
+  EXPECT_EQ(15U, header.string_ids_size_);
+  EXPECT_EQ(112U, header.string_ids_off_);
+  EXPECT_EQ(7U, header.type_ids_size_);
+  EXPECT_EQ(172U, header.type_ids_off_);
+  EXPECT_EQ(2U, header.proto_ids_size_);
+  EXPECT_EQ(200U, header.proto_ids_off_);
+  EXPECT_EQ(1U, header.field_ids_size_);
+  EXPECT_EQ(224U, header.field_ids_off_);
+  EXPECT_EQ(3U, header.method_ids_size_);
+  EXPECT_EQ(232U, header.method_ids_off_);
+  EXPECT_EQ(2U, header.class_defs_size_);
+  EXPECT_EQ(256U, header.class_defs_off_);
+  EXPECT_EQ(584U, header.data_size_);
+  EXPECT_EQ(320U, header.data_off_);
 }
 
 TEST(RawDexFile, ClassDefs) {
-  RawDexFile* raw = RawDexFile::Open(filename);
+  scoped_ptr<RawDexFile> raw(RawDexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(raw != NULL);
   EXPECT_EQ(2U, raw->NumClassDefs());
 
@@ -26,8 +76,6 @@
 
   const RawDexFile::ClassDef& c1 = raw->GetClassDef(1);
   EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c1));
-
-  delete raw;
 }
 
 }  // namespace art
diff --git a/src/scoped_ptr.h b/src/scoped_ptr.h
index 19c8166..ce21cc5 100644
--- a/src/scoped_ptr.h
+++ b/src/scoped_ptr.h
@@ -18,6 +18,8 @@
 //  implementation of the scoped_ptr class, and its closely-related brethren,
 //  scoped_array, scoped_ptr_malloc, and make_scoped_ptr.
 
+#include "src/macros.h"
+
 #include <assert.h>
 #include <stdlib.h>