/*
 * Copyright 2012, 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.
 */

#include "bcinfo/Wrap/bitcode_wrapperer.h"

#define LOG_TAG "bcinfo"

#include <stdio.h>
#include <sys/stat.h>

#include <log/log.h>

using std::vector;

// The number of bytes in a 32 bit integer.
static const uint32_t kWordSize = 4;

// Number of LLVM-defined fixed fields in the header.
static const uint32_t kLLVMFields = 4;

// Total number of fixed fields in the header.
static const uint32_t kFixedFields = 7;

// The magic number that must exist for bitcode wrappers.
static const uint32_t kWrapperMagicNumber = 0x0B17C0DE;

// The version number associated with a wrapper file.
// Note: llvm currently only allows the value 0. When this changes,
// we should consider making this a command line option.
static const uint32_t kLLVMVersionNumber = 0;

// Fields defined by Android bitcode header.
static const uint32_t kAndroidHeaderVersion = 0;
static const uint32_t kAndroidTargetAPI = 0;
static const uint32_t kAndroidDefaultCompilerVersion = 0;
static const uint32_t kAndroidDefaultOptimizationLevel = 3;

// PNaCl bitcode version number.
static const uint32_t kPnaclBitcodeVersion = 0;

// Max size for variable fields. Currently only used for writing them
// out to files (the parsing works for arbitrary sizes).
static const size_t kMaxVariableFieldSize = 256;

BitcodeWrapperer::BitcodeWrapperer(WrapperInput* infile, WrapperOutput* outfile)
    : infile_(infile),
      outfile_(outfile),
      buffer_size_(0),
      cursor_(0),
      infile_at_eof_(false),
      infile_bc_offset_(0),
      wrapper_bc_offset_(0),
      wrapper_bc_size_(0),
      android_header_version_(kAndroidHeaderVersion),
      android_target_api_(kAndroidTargetAPI),
      android_compiler_version_(kAndroidDefaultCompilerVersion),
      android_optimization_level_(kAndroidDefaultOptimizationLevel),
      pnacl_bc_version_(0),
      error_(false) {
  buffer_.resize(kBitcodeWrappererBufferSize);
  if (IsInputBitcodeWrapper()) {
    ParseWrapperHeader();
  } else if (IsInputBitcodeFile()) {
    wrapper_bc_offset_ = kWordSize * kFixedFields;
    wrapper_bc_size_ = GetInFileSize();
  } else {
    ALOGE("Error: input file is not a bitcode file.\n");
    error_ = true;
  }
}

BitcodeWrapperer::~BitcodeWrapperer() {
  for(size_t i = 0; i < variable_field_data_.size(); i++) {
    delete [] variable_field_data_[i];
  }
}


void BitcodeWrapperer::ClearBuffer() {
  buffer_size_ = 0;
  cursor_ = 0;
  infile_at_eof_ = false;
}

bool BitcodeWrapperer::Seek(uint32_t pos) {
  if (infile_ != nullptr && infile_->Seek(pos)) {
    ClearBuffer();
    return true;
  }
  return false;
}

bool BitcodeWrapperer::CanReadWord() {
  if (GetBufferUnreadBytes() < kWordSize) {
    FillBuffer();
    return GetBufferUnreadBytes() >= kWordSize;
  } else {
    return true;
  }
}

void BitcodeWrapperer::FillBuffer() {
  if (cursor_ > 0) {
    // Before filling, move any remaining bytes to the
    // front of the buffer. This allows us to assume
    // that after the call to FillBuffer, readable
    // text is contiguous.
    if (cursor_ < buffer_size_) {
      size_t i = 0;
      while (cursor_ < buffer_size_) {
        buffer_[i++] = buffer_[cursor_++];
      }
      cursor_ = 0;
      buffer_size_ = i;
    }
  } else {
    // Assume the buffer contents have been used,
    // and we want to completely refill it.
    buffer_size_ = 0;
  }

  // If we don't have an input, we can't refill the buffer at all.
  if (infile_ == nullptr) {
    return;
  }

  // Now fill in remaining space.
  size_t needed = buffer_.size() - buffer_size_;

  while (buffer_.size() > buffer_size_) {
    int actually_read = infile_->Read(&buffer_[buffer_size_], needed);
    if (infile_->AtEof()) {
      infile_at_eof_ = true;
    }
    if (actually_read) {
      buffer_size_ += actually_read;
      needed -= actually_read;
    } else if (infile_at_eof_) {
      break;
    }
  }
}

bool BitcodeWrapperer::ReadWord(uint32_t& word) {
  if (!CanReadWord()) return false;
  word = (((uint32_t) BufferLookahead(0)) << 0)
      | (((uint32_t) BufferLookahead(1)) << 8)
      | (((uint32_t) BufferLookahead(2)) << 16)
      | (((uint32_t) BufferLookahead(3)) << 24);
  cursor_ += kWordSize;
  return true;
}

bool BitcodeWrapperer::WriteWord(uint32_t value) {
  uint8_t buffer[kWordSize];
  buffer[3] = (value >> 24) & 0xFF;
  buffer[2] = (value >> 16) & 0xFF;
  buffer[1] = (value >> 8)  & 0xFF;
  buffer[0] = (value >> 0)  & 0xFF;
  return outfile_->Write(buffer, kWordSize);
}

bool BitcodeWrapperer::WriteVariableFields() {
  // This buffer may have to be bigger if we start using the fields
  // for larger things.
  uint8_t buffer[kMaxVariableFieldSize];
  for (vector<BCHeaderField>::iterator it = header_fields_.begin();
       it != header_fields_.end(); ++it) {
    if (!it->Write(buffer, kMaxVariableFieldSize) ||
        !outfile_->Write(buffer, it->GetTotalSize())) {
      return false;
    }
  }
  return true;
}

bool BitcodeWrapperer::ParseWrapperHeader() {
  // Make sure LLVM-defined fields have been parsed
  if (!IsInputBitcodeWrapper()) return false;
  // Check the android/pnacl fields
  if (!ReadWord(android_header_version_) ||
      !ReadWord(android_target_api_) || !ReadWord(pnacl_bc_version_)) {
    ALOGW("Error: file not long enough to contain header\n");
    return false;
  }
  if (pnacl_bc_version_ != kPnaclBitcodeVersion) {
    ALOGW("Error: bad PNaCl Bitcode version\n");
    return false;
  }
  int field_data_total = wrapper_bc_offset_ - kWordSize * kFixedFields;
  if (field_data_total > 0) {
    // Read in the variable fields. We need to allocate space for the data.
    int field_data_read = 0;

    while (field_data_read < field_data_total) {
      FillBuffer();
      size_t buffer_needed = BCHeaderField::GetDataSizeFromSerialized(
          &buffer_[cursor_]);
      if (buffer_needed > buffer_.size()) {
        buffer_.resize(buffer_needed +
                       sizeof(BCHeaderField::FixedSubfield) * 2);
        FillBuffer();
      }
      variable_field_data_.push_back(new uint8_t[buffer_needed]);

      BCHeaderField field(BCHeaderField::kInvalid, 0,
                          variable_field_data_.back());
      field.Read(&buffer_[cursor_], buffer_size_);
      header_fields_.push_back(field);
      size_t field_size = field.GetTotalSize();
      cursor_ += field_size;
      field_data_read += field_size;
      if (field_data_read > field_data_total) {
        // We read too much data, the header is corrupted
        ALOGE("Error: raw bitcode offset inconsistent with "
              "variable field data\n");
        return false;
      }

      struct IntFieldHelper {
        BCHeaderField::FixedSubfield tag;
        uint16_t len;
        uint32_t val;
      };
      IntFieldHelper tempIntField;

      switch (field.getID()) {
        case BCHeaderField::kAndroidCompilerVersion:
          if (field.Write((uint8_t*)&tempIntField,
                          sizeof(tempIntField))) {
            android_compiler_version_ = tempIntField.val;
          }
          break;
        case BCHeaderField::kAndroidOptimizationLevel:
          if (field.Write((uint8_t*)&tempIntField,
                          sizeof(tempIntField))) {
            android_optimization_level_ = tempIntField.val;
          }
          break;
        default:
          // Ignore other field types for now
          break;
      }
    }
    Seek(0);
  }
  return true;
}

bool BitcodeWrapperer::IsInputBitcodeWrapper() {
  ResetCursor();
  // First make sure that there are enough words (LLVM header)
  // to peek at.
  if (GetBufferUnreadBytes() < kLLVMFields * kWordSize) {
    FillBuffer();
    if (GetBufferUnreadBytes() < kLLVMFields * kWordSize) return false;
  }

  // Now make sure the magic number is right.
  uint32_t first_word;
  if ((!ReadWord(first_word)) ||
      (kWrapperMagicNumber != first_word)) return false;

  // Make sure the version is right.
  uint32_t second_word;
  if ((!ReadWord(second_word)) ||
      (kLLVMVersionNumber != second_word)) return false;

  // Make sure that the offset and size (for llvm) is defined.
  uint32_t bc_offset;
  uint32_t bc_size;
  if (ReadWord(bc_offset) &&
      ReadWord(bc_size)) {
    // Before returning, save the extracted values.
    wrapper_bc_offset_ = bc_offset;
    infile_bc_offset_ = bc_offset;
    wrapper_bc_size_ = bc_size;
    return true;
  }
  // If reached, unable to read wrapped header.
  return false;
}

bool BitcodeWrapperer::IsInputBitcodeFile() {
  ResetCursor();
  // First make sure that there are four bytes to peek at.
  if (GetBufferUnreadBytes() < kWordSize) {
    FillBuffer();
    if (GetBufferUnreadBytes() < kWordSize) return false;
  }
  // If reached, Check if first 4 bytes match bitcode
  // file magic number.
  return (BufferLookahead(0) == 'B') &&
      (BufferLookahead(1) == 'C') &&
      (BufferLookahead(2) == 0xc0) &&
      (BufferLookahead(3) == 0xde);
}

bool BitcodeWrapperer::BufferCopyInToOut(uint32_t size) {
  while (size > 0) {
    // Be sure buffer is non-empty before writing.
    if (0 == buffer_size_) {
      FillBuffer();
      if (0 == buffer_size_) {
        return false;
      }
    }
    // copy the buffer to the output file.
    size_t block = (buffer_size_ < size) ? buffer_size_ : size;
    if (!outfile_->Write(&buffer_[cursor_], block)) return false;
    size -= block;
    buffer_size_ = 0;
  }
  // Be sure that there isn't more bytes on the input stream.
  FillBuffer();
  return buffer_size_ == 0;
}

void BitcodeWrapperer::AddHeaderField(BCHeaderField* field) {
  header_fields_.push_back(*field);
  wrapper_bc_offset_ += field->GetTotalSize();
}

bool BitcodeWrapperer::WriteBitcodeWrapperHeader() {
  return
      // Note: This writes out the 4 word header required by llvm wrapped
      // bitcode.
      WriteWord(kWrapperMagicNumber) &&
      WriteWord(kLLVMVersionNumber) &&
      WriteWord(wrapper_bc_offset_) &&
      WriteWord(wrapper_bc_size_) &&
      // 2 fixed fields defined by Android
      WriteWord(android_header_version_) &&
      WriteWord(android_target_api_) &&
      // PNaClBitcode version
      WriteWord(kPnaclBitcodeVersion) &&
      // Common variable-length fields
      WriteVariableFields();
}

void BitcodeWrapperer::PrintWrapperHeader() {
  if (error_) {
    fprintf(stderr, "Error condition exists: the following"
            "data may not be reliable\n");
  }
  fprintf(stderr, "Wrapper magic:\t\t%x\n", kWrapperMagicNumber);
  fprintf(stderr, "LLVM Bitcode version:\t%d\n", kLLVMVersionNumber);
  fprintf(stderr, "Raw bitcode offset:\t%d\n", wrapper_bc_offset_);
  fprintf(stderr, "Raw bitcode size:\t%d\n", wrapper_bc_size_);
  fprintf(stderr, "Android header version:\t%d\n", android_header_version_);
  fprintf(stderr, "Android target API:\t%d\n", android_target_api_);
  fprintf(stderr, "PNaCl bitcode version:\t%d\n", kPnaclBitcodeVersion);
  for (size_t i = 0; i < header_fields_.size(); i++) header_fields_[i].Print();
}

bool BitcodeWrapperer::GenerateWrappedBitcodeFile() {
  if (!error_ &&
      WriteBitcodeWrapperHeader() &&
      Seek(infile_bc_offset_) &&
      BufferCopyInToOut(wrapper_bc_size_)) {
    off_t dangling = wrapper_bc_size_ & 3;
    if (dangling) {
      return outfile_->Write((const uint8_t*) "\0\0\0\0", 4 - dangling);
    }
    return true;
  }
  return false;
}

bool BitcodeWrapperer::GenerateRawBitcodeFile() {
  return !error_ && Seek(infile_bc_offset_) &&
      BufferCopyInToOut(wrapper_bc_size_);
}