Add CacheWriter.
diff --git a/lib/bcc/CacheWriter.cpp b/lib/bcc/CacheWriter.cpp
new file mode 100644
index 0000000..e51d468
--- /dev/null
+++ b/lib/bcc/CacheWriter.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bcc"
+#include <cutils/log.h>
+
+#include "CacheWriter.h"
+
+#include "ContextManager.h"
+#include "FileHandle.h"
+#include "Script.h"
+
+#include <map>
+#include <string>
+#include <vector>
+#include <utility>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+using namespace std;
+
+namespace bcc {
+
+bool CacheWriter::writeCacheFile(FileHandle *file, Script *S,
+                                 uint32_t libRS_threadable) {
+  if (!file || file->getFD() < 0) {
+    return false;
+  }
+
+  mFile = file;
+  mpOwner = S;
+
+  bool result = prepareHeader(libRS_threadable)
+             && prepareDependencyTable()
+             && prepareFuncTable()
+             && preparePragmaList()
+             //&& prepareRelocationTable()
+             && prepareStringPool()
+             && prepareExportVarList()
+             && prepareExportFuncList()
+             && calcSectionOffset()
+             && calcContextChecksum()
+             && writeAll()
+             ;
+
+  return result;
+}
+
+
+bool CacheWriter::prepareHeader(uint32_t libRS_threadable) {
+  OBCC_Header *header = (OBCC_Header *)malloc(sizeof(OBCC_Header));
+
+  if (!header) {
+    LOGE("Unable to allocate for header.\n");
+    return false;
+  }
+
+  mpHeaderSection = header;
+
+  // Initialize
+  memset(header, '\0', sizeof(OBCC_Header));
+
+  // Magic word and version 
+  memcpy(header->magic, OBCC_MAGIC, 4);
+  memcpy(header->version, OBCC_VERSION, 4);
+
+  // Machine Integer Type
+  uint32_t number = 0x00000001;
+  header->endianness = (*reinterpret_cast<char *>(&number) == 1) ? 'e' : 'E';
+  header->sizeof_off_t = sizeof(off_t);
+  header->sizeof_size_t = sizeof(size_t);
+  header->sizeof_ptr_t = sizeof(void *);
+
+  // Context
+  header->context_cached_addr = mpOwner->getContext();
+
+  // libRS is threadable dirty hack
+  // TODO: This should be removed in the future
+  header->libRS_threadable = libRS_threadable;
+
+  return true;
+}
+
+
+bool CacheWriter::prepareDependencyTable() {
+  size_t tableSize = sizeof(OBCC_DependencyTable) +
+                     sizeof(OBCC_Dependency) * mDependencies.size();
+
+  OBCC_DependencyTable *tab = (OBCC_DependencyTable *)malloc(tableSize);
+  
+  if (!tab) {
+    LOGE("Unable to allocate for dependency table section.\n");
+    return false;
+  }
+
+  mpDependencyTableSection = tab;
+  mpHeaderSection->depend_tab_size = tableSize;
+
+  tab->count = mDependencies.size();
+
+  size_t i = 0;
+  for (map<string, pair<uint32_t, unsigned char const *> >::iterator
+       I = mDependencies.begin(), E = mDependencies.end(); I != E; ++I, ++i) {
+    OBCC_Dependency *dep = &tab->table[i];
+
+    dep->res_name_strp_index = addString(I->first.c_str(), I->first.size());
+    dep->res_type = I->second.first;
+    memcpy(dep->sha1, I->second.second, 20);
+  }
+
+  return true;
+}
+
+
+bool CacheWriter::prepareFuncTable() {
+  ssize_t funcCount = 0;
+
+  mpOwner->getFunctions(&funcCount, 0, NULL);
+
+  size_t tableSize = sizeof(OBCC_FuncTable) +
+                     sizeof(OBCC_FuncInfo) * funcCount;
+
+  OBCC_FuncTable *tab = (OBCC_FuncTable *)malloc(tableSize);
+
+  if (!tab) {
+    LOGE("Unable to allocate for function table section.\n");
+    return false;
+  }
+
+  mpFuncTableSection = tab;
+  mpHeaderSection->func_table_size = tableSize; 
+
+  tab->count = static_cast<size_t>(funcCount);
+
+  // Get the function informations 
+  vector<char *> funcNameList(funcCount);
+  mpOwner->getFunctions(0, funcCount, &*funcNameList.begin());
+
+  for (int i = 0; i < funcCount; ++i) {
+    char *funcName = funcNameList[i];
+    size_t funcNameLen = strlen(funcName);
+
+    void *funcAddr = NULL;
+    ssize_t funcBinarySize = 0;
+    mpOwner->getFunctionBinary(funcName, &funcAddr, &funcBinarySize);
+
+    OBCC_FuncInfo *funcInfo = &tab->table[i];
+    funcInfo->name_strp_index = addString(funcName, funcNameLen);
+    funcInfo->cached_addr = funcAddr;
+    funcInfo->size = static_cast<size_t>(funcBinarySize);
+  }
+
+  return true;
+}
+
+
+bool CacheWriter::preparePragmaList() {
+  ssize_t stringCount;
+
+  mpOwner->getPragmas(&stringCount, 0, NULL);
+
+  size_t pragmaCount = static_cast<size_t>(stringCount) / 2;
+
+  size_t listSize = sizeof(OBCC_PragmaList) +
+                    sizeof(OBCC_Pragma) * pragmaCount;
+
+  OBCC_PragmaList *list = (OBCC_PragmaList *)malloc(listSize);
+
+  if (!list) {
+    LOGE("Unable to allocate for pragma list\n");
+    return false;
+  }
+
+  mpPragmaListSection = list;
+  mpHeaderSection->pragma_list_size = listSize;
+
+  list->count = pragmaCount;
+
+  vector<char *> strings(stringCount);
+  mpOwner->getPragmas(&stringCount, stringCount, &*strings.begin());
+
+  for (size_t i = 0; i < pragmaCount; ++i) {
+    char *key = strings[2 * i];
+    size_t keyLen = strlen(key);
+
+    char *value = strings[2 * i + 1];
+    size_t valueLen = strlen(value);
+
+    OBCC_Pragma *pragma = &list->list[i];
+    pragma->key_strp_index = addString(key, keyLen);
+    pragma->value_strp_index = addString(value, valueLen);
+  }
+
+  return true;
+}
+
+
+bool CacheWriter::prepareRelocationTable() {
+  // TODO(logan): Implement relocation table cache write.
+  return false;
+}
+
+
+bool CacheWriter::prepareStringPool() {
+  // Calculate string pool size
+  size_t size = sizeof(OBCC_StringPool) +
+                sizeof(OBCC_String) * mStringPool.size();
+
+  off_t strOffset = size;
+
+  for (size_t i = 0; i < mStringPool.size(); ++i) {
+    size += mStringPool[i].second + 1;
+  }
+
+  // Create string pool
+  OBCC_StringPool *pool = (OBCC_StringPool *)malloc(size);
+
+  if (!pool) {
+    LOGE("Unable to allocate string pool.\n");
+    return false;
+  }
+
+  mpStringPoolSection = pool;
+  mpHeaderSection->str_pool_size = size;
+
+  char *strPtr = reinterpret_cast<char *>(pool) + strOffset;
+
+  for (size_t i = 0; i < mStringPool.size(); ++i) {
+    OBCC_String *str = &pool->list[i];
+
+    str->length = mStringPool[i].second;
+    str->offset = strOffset;
+    memcpy(strPtr, mStringPool[i].first, str->length);
+
+    strPtr += str->length;
+    *strPtr++ = '\0';
+
+    strOffset += str->length;
+  }
+
+  return true;
+}
+
+
+bool CacheWriter::prepareExportVarList() {
+  ssize_t varCount;
+
+  mpOwner->getExportVars(&varCount, 0, NULL);
+
+  size_t listSize = sizeof(OBCC_ExportVarList) + sizeof(void *) * varCount;
+
+  OBCC_ExportVarList *list = (OBCC_ExportVarList *)malloc(listSize);
+
+  if (!list) {
+    LOGE("Unable to allocate for export variable list\n");
+    return false;
+  }
+
+  mpExportVarListSection = list;
+  mpHeaderSection->export_var_list_size = listSize;
+
+  list->count = static_cast<size_t>(varCount);
+
+  mpOwner->getExportVars(&varCount, varCount, list->cached_addr_list);
+  return true;
+}
+
+
+bool CacheWriter::prepareExportFuncList() {
+  ssize_t funcCount;
+
+  mpOwner->getExportFuncs(&funcCount, 0, NULL);
+
+  size_t listSize = sizeof(OBCC_ExportFuncList) + sizeof(void *) * funcCount;
+
+  OBCC_ExportFuncList *list = (OBCC_ExportFuncList *)malloc(listSize);
+
+  if (!list) {
+    LOGE("Unable to allocate for export function list\n");
+    return false;
+  }
+
+  mpExportFuncListSection = list;
+  mpHeaderSection->export_func_list_size = listSize;
+
+  list->count = static_cast<size_t>(funcCount);
+
+  mpOwner->getExportFuncs(&funcCount, funcCount, list->cached_addr_list);
+  return true;
+}
+
+
+bool CacheWriter::calcSectionOffset() {
+  size_t offset = sizeof(OBCC_Header);
+
+#define OFFSET_INCREASE(NAME)                                               \
+  do {                                                                      \
+    /* Align to a word */                                                   \
+    size_t rem = offset % sizeof(int);                                      \
+    if (rem > 0) {                                                          \
+      offset += sizeof(int) - rem;                                          \
+    }                                                                       \
+                                                                            \
+    /* Save the offset and increase it */                                   \
+    mpHeaderSection->NAME##_offset = offset;                                \
+    offset += mpHeaderSection->NAME##_size;                                 \
+  } while (0)
+
+  OFFSET_INCREASE(str_pool);
+  OFFSET_INCREASE(depend_tab);
+  //OFFSET_INCREASE(reloc_tab);
+  OFFSET_INCREASE(export_var_list);
+  OFFSET_INCREASE(export_func_list);
+  OFFSET_INCREASE(pragma_list);
+  OFFSET_INCREASE(func_table);
+
+#undef OFFSET_INCREASE
+
+  // Context
+  long pagesize = sysconf(_SC_PAGESIZE);
+  size_t context_offset_rem = offset % pagesize;
+  if (context_offset_rem) {
+    offset += pagesize - context_offset_rem;
+  }
+
+  mpHeaderSection->context_offset = offset;
+  return true;
+}
+
+
+bool CacheWriter::calcContextChecksum() {
+  uint32_t sum = 0;
+  uint32_t *ptr = reinterpret_cast<uint32_t *>(mpOwner->getContext());
+
+  for (size_t i = 0; i < BCC_CONTEXT_SIZE / sizeof(uint32_t); ++i) {
+    sum ^= *ptr++;
+  }
+
+  mpHeaderSection->context_parity_checksum = sum;
+  return true;
+}
+
+
+bool CacheWriter::writeAll() {
+#define WRITE_SECTION(NAME, OFFSET, SIZE, SECTION)                          \
+  do {                                                                      \
+    if (mFile->seek(OFFSET, SEEK_SET) == -1) {                              \
+      LOGE("Unable to seek to " #NAME " section for writing.\n");           \
+      return false;                                                         \
+    }                                                                       \
+                                                                            \
+    if (mFile->write(reinterpret_cast<char *>(SECTION), (SIZE)) !=          \
+        static_cast<ssize_t>(SIZE)) {                                       \
+      LOGE("Unable to write " #NAME " section to cache file.\n");           \
+      return false;                                                         \
+    }                                                                       \
+  } while (0)
+
+#define WRITE_SECTION_SIMPLE(NAME, SECTION)                                 \
+  WRITE_SECTION(NAME,                                                       \
+                mpHeaderSection->NAME##_offset,                             \
+                mpHeaderSection->NAME##_size,                               \
+                SECTION)
+
+  WRITE_SECTION(header, 0, sizeof(OBCC_Header), mpHeaderSection);
+
+  WRITE_SECTION_SIMPLE(str_pool, mpStringPoolSection);
+  WRITE_SECTION_SIMPLE(depend_tab, mpDependencyTableSection);
+  //WRITE_SECTION_SIMPLE(reloc_tab, mpRelocationTableSection);
+  WRITE_SECTION_SIMPLE(export_var_list, mpExportVarListSection);
+  WRITE_SECTION_SIMPLE(export_func_list, mpExportFuncListSection);
+  WRITE_SECTION_SIMPLE(pragma_list, mpPragmaListSection);
+  WRITE_SECTION_SIMPLE(func_table, mpFuncTableSection);
+
+  WRITE_SECTION(context, mpHeaderSection->context_offset, BCC_CONTEXT_SIZE,
+                mpOwner->getContext());
+
+#undef WRITE_SECTION_SIMPLE
+#undef WRITE_SECTION
+
+  return true;
+}
+
+
+#if 0
+void Compiler::genCacheFile() {
+
+  // Write Header
+  sysWriteFully(mCacheFd, reinterpret_cast<char const *>(hdr),
+                sizeof(oBCCHeader), "Write oBCC header");
+
+  // Write Relocation Entry Table
+  {
+    size_t allocSize = hdr->relocCount * sizeof(oBCCRelocEntry);
+
+    oBCCRelocEntry const*records = &mCodeEmitter->getCachingRelocations()[0];
+
+    sysWriteFully(mCacheFd, reinterpret_cast<char const *>(records),
+                  allocSize, "Write Relocation Entries");
+  }
+
+  // Write Export Variables Table
+  {
+    uint32_t *record, *ptr;
+
+    record = (uint32_t *)calloc(hdr->exportVarsCount, sizeof(uint32_t));
+    ptr = record;
+
+    if (!record) {
+      goto bail;
+    }
+
+    for (ScriptCompiled::ExportVarList::const_iterator
+         I = mpResult->mExportVars.begin(),
+         E = mpResult->mExportVars.end(); I != E; I++) {
+      *ptr++ = reinterpret_cast<uint32_t>(*I);
+    }
+
+    sysWriteFully(mCacheFd, reinterpret_cast<char const *>(record),
+                  hdr->exportVarsCount * sizeof(uint32_t),
+                  "Write ExportVars");
+
+    free(record);
+  }
+
+  // Write Export Functions Table
+  {
+    uint32_t *record, *ptr;
+
+    record = (uint32_t *)calloc(hdr->exportFuncsCount, sizeof(uint32_t));
+    ptr = record;
+
+    if (!record) {
+      goto bail;
+    }
+
+    for (ScriptCompiled::ExportFuncList::const_iterator
+         I = mpResult->mExportFuncs.begin(),
+         E = mpResult->mExportFuncs.end(); I != E; I++) {
+      *ptr++ = reinterpret_cast<uint32_t>(*I);
+    }
+
+    sysWriteFully(mCacheFd, reinterpret_cast<char const *>(record),
+                  hdr->exportFuncsCount * sizeof(uint32_t),
+                  "Write ExportFuncs");
+
+    free(record);
+  }
+
+
+  // Write Export Pragmas Table
+  {
+    uint32_t pragmaEntryOffset =
+      hdr->exportPragmasCount * sizeof(oBCCPragmaEntry);
+
+    for (ScriptCompiled::PragmaList::const_iterator
+         I = mpResult->mPragmas.begin(),
+         E = mpResult->mPragmas.end(); I != E; ++I) {
+      oBCCPragmaEntry entry;
+
+      entry.pragmaNameOffset = pragmaEntryOffset;
+      entry.pragmaNameSize = I->first.size();
+      pragmaEntryOffset += entry.pragmaNameSize + 1;
+
+      entry.pragmaValueOffset = pragmaEntryOffset;
+      entry.pragmaValueSize = I->second.size();
+      pragmaEntryOffset += entry.pragmaValueSize + 1;
+
+      sysWriteFully(mCacheFd, (char *)&entry, sizeof(oBCCPragmaEntry),
+                    "Write export pragma entry");
+    }
+
+    for (ScriptCompiled::PragmaList::const_iterator
+         I = mpResult->mPragmas.begin(),
+         E = mpResult->mPragmas.end(); I != E; ++I) {
+      sysWriteFully(mCacheFd, I->first.c_str(), I->first.size() + 1,
+                    "Write export pragma name string");
+      sysWriteFully(mCacheFd, I->second.c_str(), I->second.size() + 1,
+                    "Write export pragma value string");
+    }
+  }
+
+  if (codeOffsetNeedPadding) {
+    // requires additional padding
+    lseek(mCacheFd, hdr->codeOffset, SEEK_SET);
+  }
+
+  // Write Generated Code and Global Variable
+  sysWriteFully(mCacheFd, mCodeDataAddr, MaxCodeSize + MaxGlobalVarSize,
+                "Write code and global variable");
+
+  goto close_return;
+
+bail:
+  if (ftruncate(mCacheFd, 0) != 0) {
+    LOGW("Warning: unable to truncate cache file: %s\n", strerror(errno));
+  }
+
+close_return:
+  free(hdr);
+  close(mCacheFd);
+  mCacheFd = -1;
+}
+#endif
+
+} // namespace bcc
diff --git a/lib/bcc/CacheWriter.h b/lib/bcc/CacheWriter.h
new file mode 100644
index 0000000..b6d05f6
--- /dev/null
+++ b/lib/bcc/CacheWriter.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BCC_CACHEWRITER_H
+#define BCC_CACHEWRITER_H
+
+#include <bcc/bcc_cache.h>
+
+#include "FileHandle.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace bcc {
+  class Script;
+
+  class CacheWriter {
+  private:
+    Script *mpOwner;
+
+    FileHandle *mFile;
+
+    std::vector<std::pair<char const *, size_t> > mStringPool;
+
+    std::map<std::string,
+             std::pair<uint32_t, unsigned char const *> > mDependencies;
+
+    OBCC_Header *mpHeaderSection;
+    OBCC_StringPool *mpStringPoolSection;
+    OBCC_DependencyTable *mpDependencyTableSection;
+    //OBCC_RelocationTable *mpRelocationTableSection;
+    OBCC_ExportVarList *mpExportVarListSection;
+    OBCC_ExportFuncList *mpExportFuncListSection;
+    OBCC_PragmaList *mpPragmaListSection;
+    OBCC_FuncTable *mpFuncTableSection;
+
+  public:
+    CacheWriter()
+      : mpHeaderSection(NULL), mpStringPoolSection(NULL),
+        mpDependencyTableSection(NULL), mpExportVarListSection(NULL),
+        mpExportFuncListSection(NULL), mpPragmaListSection(NULL),
+        mpFuncTableSection(NULL) {
+    }
+
+    ~CacheWriter();
+
+    bool writeCacheFile(FileHandle *file, Script *S,
+                        uint32_t libRS_threadable);
+
+    void addDependency(OBCC_ResourceType resType,
+                       std::string const &resName,
+                       unsigned char const *sha1) {
+      mDependencies.insert(std::make_pair(resName,
+                           std::make_pair((uint32_t)resType, sha1)));
+    }
+
+  private:
+    bool prepareHeader(uint32_t libRS_threadable);
+    bool prepareStringPool();
+    bool prepareDependencyTable();
+    bool prepareRelocationTable();
+    bool prepareExportVarList();
+    bool prepareExportFuncList();
+    bool preparePragmaList();
+    bool prepareFuncTable();
+
+    bool writeAll();
+
+    bool calcSectionOffset();
+    bool calcContextChecksum();
+
+    size_t addString(char const *str, size_t size) {
+      mStringPool.push_back(std::make_pair(str, size));
+      return mStringPool.size() - 1;
+    }
+
+  };
+
+} // namespace bcc
+
+#endif // BCC_CACHEWRITER_H
diff --git a/lib/bcc/Script.cpp b/lib/bcc/Script.cpp
index 789fb01..8ad7844 100644
--- a/lib/bcc/Script.cpp
+++ b/lib/bcc/Script.cpp
@@ -270,8 +270,18 @@
 
     if (file.open(cacheFile, OpenMode::Write) >= 0) {
       CacheWriter writer;
-      writer.writeCacheFile(&file);
+
+      // libRS is threadable dirty hack
+      // TODO: This should be removed in the future
+      uint32_t libRS_threadable = 0;
+      if (mpExtSymbolLookupFn) {
+        libRS_threadable = mpExtSymbolLookupFn(mpExtSymbolLookupContext,
+                                               "__isThreadable");
+      }
+
+      writer.writeCacheFile(&file, this, libRS_threadable);
     }
+
   }
 #endif
 
@@ -346,13 +356,18 @@
   mCompiled->getFunctions(actualFunctionCount, maxFunctionCount, functions);
 }
 
-char const *Script::getContext() const {
-  if (mStatus != ScriptStatus::Compiled) {
-    //mErrorCode = BCC_INVALID_OPERATION;
+char *Script::getContext() {
+  switch (mStatus) {
+  case ScriptStatus::Compiled:
+    return mCompiled->getContext();
+
+  case ScriptStatus::Cached:
+    return mCached->getContext();
+
+  default:
+    mErrorCode = BCC_INVALID_OPERATION;
     return NULL;
   }
-
-  return mCompiled->getContext();
 }
 
 
diff --git a/lib/bcc/Script.h b/lib/bcc/Script.h
index 8f16de5..8b3beef 100644
--- a/lib/bcc/Script.h
+++ b/lib/bcc/Script.h
@@ -112,7 +112,7 @@
                            BCCvoid **base,
                            BCCsizei *length);
 
-    char const *getContext() const;
+    char *getContext();
 
     void registerSymbolCallback(BCCSymbolLookupFn pFn, BCCvoid *pContext);
 
diff --git a/lib/bcc/ScriptCached.h b/lib/bcc/ScriptCached.h
index fed5740..8950a11 100644
--- a/lib/bcc/ScriptCached.h
+++ b/lib/bcc/ScriptCached.h
@@ -93,6 +93,10 @@
                            BCCvoid **base,
                            BCCsizei *length);
 
+    char *getContext() {
+      return mContext;
+    }
+
     // Dirty hack for libRS.
     // TODO(all): This should be removed in the future.
     bool isLibRSThreadable() const {
diff --git a/lib/bcc/ScriptCompiled.h b/lib/bcc/ScriptCompiled.h
index ce0ad9c..f676b3c 100644
--- a/lib/bcc/ScriptCompiled.h
+++ b/lib/bcc/ScriptCompiled.h
@@ -105,7 +105,7 @@
                            BCCvoid **base,
                            BCCsizei *length);
 
-    char const *getContext() const {
+    char *getContext() {
       return mContext;
     }