<commit_before>/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "data_log.h"

#include <assert.h>

#include <algorithm>
#include <list>

#include "critical_section_wrapper.h"
#include "event_wrapper.h"
#include "file_wrapper.h"
#include "rw_lock_wrapper.h"
#include "thread_wrapper.h"

namespace webrtc {

DataLogImpl::CritSectScopedPtr DataLogImpl::crit_sect_(
  CriticalSectionWrapper::CreateCriticalSection());

DataLogImpl* DataLogImpl::instance_ = NULL;

// A Row contains cells, which are indexed by the column names as std::string.
// The string index is treated in a case sensitive way.
class Row {
 public:
  Row();
  ~Row();

  // Inserts a Container into the cell of the column specified with
  // column_name.
  // column_name is treated in a case sensitive way.
  int InsertCell(const std::string& column_name,
                 const Container* value_container);

  // Converts the value at the column specified by column_name to a string
  // stored in value_string.
  // column_name is treated in a case sensitive way.
  void ToString(const std::string& column_name, std::string* value_string);

 private:
  // Collection of containers indexed by column name as std::string
  typedef std::map<std::string, const Container*> CellMap;

  CellMap                   cells_;
  CriticalSectionWrapper*   cells_lock_;
};

// A LogTable contains multiple rows, where only the latest row is active for
// editing. The rows are defined by the ColumnMap, which contains the name of
// each column and the length of the column (1 for one-value-columns and greater
// than 1 for multi-value-columns).
class LogTable {
 public:
  LogTable();
  ~LogTable();

  // Adds the column with name column_name to the table. The column will be a
  // multi-value-column if multi_value_length is greater than 1.
  // column_name is treated in a case sensitive way.
  int AddColumn(const std::string& column_name, int multi_value_length);

  // Buffers the current row while it is waiting to be written to file,
  // which is done by a call to Flush(). A new row is available when the
  // function returns
  void NextRow();

  // Inserts a Container into the cell of the column specified with
  // column_name.
  // column_name is treated in a case sensitive way.
  int InsertCell(const std::string& column_name,
                 const Container* value_container);

  // Creates a log file, named as specified in the string file_name, to
  // where the table will be written when calling Flush().
  int CreateLogFile(const std::string& file_name);

  // Write all complete rows to file.
  // May not be called by two threads simultaneously (doing so may result in
  // a race condition). Will be called by the file_writer_thread_ when that
  // thread is running.
  void Flush();

 private:
  // Collection of multi_value_lengths indexed by column name as std::string
  typedef std::map<std::string, int> ColumnMap;
  typedef std::list<Row*> RowList;

  ColumnMap               columns_;
  RowList                 rows_[2];
  RowList*                rows_history_;
  RowList*                rows_flush_;
  Row*                    current_row_;
  FileWrapper*            file_;
  bool                    write_header_;
  CriticalSectionWrapper* table_lock_;
};

Row::Row()
  : cells_(),
    cells_lock_(CriticalSectionWrapper::CreateCriticalSection()) {
}

Row::~Row() {
  for (CellMap::iterator it = cells_.begin(); it != cells_.end();) {
    delete it->second;
    // For maps all iterators (except the erased) are valid after an erase
    cells_.erase(it++);
  }
  delete cells_lock_;
}

int Row::InsertCell(const std::string& column_name,
                    const Container* value_container) {
  CriticalSectionScoped synchronize(cells_lock_);
  assert(cells_.count(column_name) == 0);
  if (cells_.count(column_name) > 0)
    return -1;
  cells_[column_name] = value_container;
  return 0;
}

void Row::ToString(const std::string& column_name,
                   std::string* value_string) {
  CriticalSectionScoped synchronize(cells_lock_);
  const Container* container = cells_[column_name];
  if (container == NULL) {
    *value_string = "NaN,";
    return;
  }
  container->ToString(value_string);
}

LogTable::LogTable()
  : columns_(),
    rows_(),
    rows_history_(&rows_[0]),
    rows_flush_(&rows_[1]),
    current_row_(new Row),
    file_(FileWrapper::Create()),
    write_header_(true),
    table_lock_(CriticalSectionWrapper::CreateCriticalSection()) {
}

LogTable::~LogTable() {
  for (RowList::iterator row_it = rows_history_->begin();
       row_it != rows_history_->end();) {
    delete *row_it;
    row_it = rows_history_->erase(row_it);
  }
  for (ColumnMap::iterator col_it = columns_.begin();
       col_it != columns_.end();) {
    // For maps all iterators (except the erased) are valid after an erase
    columns_.erase(col_it++);
  }
  if (file_ != NULL) {
    file_->Flush();
    file_->CloseFile();
    delete file_;
  }
  delete current_row_;
  delete table_lock_;
}

int LogTable::AddColumn(const std::string& column_name,
                        int multi_value_length) {
  assert(multi_value_length > 0);
  if (!write_header_) {
    // It's not allowed to add new columns after the header
    // has been written.
    assert(false);
    return -1;
  } else {
    CriticalSectionScoped synchronize(table_lock_);
    if (write_header_)
      columns_[column_name] = multi_value_length;
    else
      return -1;
  }
  return 0;
}

void LogTable::NextRow() {
  CriticalSectionScoped sync_rows(table_lock_);
  rows_history_->push_back(current_row_);
  current_row_ = new Row;
}

int LogTable::InsertCell(const std::string& column_name,
                         const Container* value_container) {
  CriticalSectionScoped synchronize(table_lock_);
  assert(columns_.count(column_name) > 0);
  if (columns_.count(column_name) == 0)
    return -1;
  return current_row_->InsertCell(column_name, value_container);
}

int LogTable::CreateLogFile(const std::string& file_name) {
  if (file_name.length() == 0)
    return -1;
  if (file_->Open())
    return -1;
  file_->OpenFile(file_name.c_str(),
                  false,  // Open with read/write permissions
                  false,  // Don't wraparound and write at the beginning when
                          // the file is full
                  true);  // Open as a text file
  if (file_ == NULL)
    return -1;
  return 0;
}

void LogTable::Flush() {
  ColumnMap::iterator column_it;
  bool commit_header = false;
  if (write_header_) {
    CriticalSectionScoped synchronize(table_lock_);
    if (write_header_) {
      commit_header = true;
      write_header_ = false;
    }
  }
  if (commit_header) {
    for (column_it = columns_.begin();
         column_it != columns_.end(); ++column_it) {
      if (column_it->second > 1) {
        file_->WriteText("%s[%u],", column_it->first.c_str(),
                         column_it->second);
        for (int i = 1; i < column_it->second; ++i)
          file_->WriteText(",");
      } else {
        file_->WriteText("%s,", column_it->first.c_str());
      }
    }
    if (columns_.size() > 0)
      file_->WriteText("\n");
  }

  // Swap the list used for flushing with the list containing the row history
  // and clear the history. We also create a local pointer to the new
  // list used for flushing to avoid race conditions if another thread
  // calls this function while we are writing.
  // We don't want to block the list while we're writing to file.
  {
    CriticalSectionScoped synchronize(table_lock_);
    RowList* tmp = rows_flush_;
    rows_flush_ = rows_history_;
    rows_history_ = tmp;
    rows_history_->clear();
  }

  // Write all complete rows to file and delete them
  for (RowList::iterator row_it = rows_flush_->begin();
       row_it != rows_flush_->end();) {
    for (column_it = columns_.begin();
         column_it != columns_.end(); ++column_it) {
      std::string row_string;
      (*row_it)->ToString(column_it->first, &row_string);
      file_->WriteText("%s", row_string.c_str());
    }
    if (columns_.size() > 0)
      file_->WriteText("\n");
    delete *row_it;
    row_it = rows_flush_->erase(row_it);
  }
}

int DataLog::CreateLog() {
  return DataLogImpl::CreateLog();
}

void DataLog::ReturnLog() {
  return DataLogImpl::ReturnLog();
}

std::string DataLog::Combine(const std::string& table_name, int table_id) {
  std::stringstream ss;
  std::string combined_id = table_name;
  std::string number_suffix;
  ss << "_" << table_id;
  ss >> number_suffix;
  combined_id += number_suffix;
  std::transform(combined_id.begin(), combined_id.end(), combined_id.begin(),
                 ::tolower);
  return combined_id;
}

int DataLog::AddTable(const std::string& table_name) {
  DataLogImpl* data_log = DataLogImpl::StaticInstance();
  if (data_log == NULL)
    return -1;
  return data_log->AddTable(table_name);
}

int DataLog::AddColumn(const std::string& table_name,
                       const std::string& column_name,
                       int multi_value_length) {
  DataLogImpl* data_log = DataLogImpl::StaticInstance();
  if (data_log == NULL)
    return -1;
  return data_log->DataLogImpl::StaticInstance()->AddColumn(table_name,
                                                            column_name,
                                                            multi_value_length);
}

int DataLog::NextRow(const std::string& table_name) {
  DataLogImpl* data_log = DataLogImpl::StaticInstance();
  if (data_log == NULL)
    return -1;
  return data_log->DataLogImpl::StaticInstance()->NextRow(table_name);
}

DataLogImpl::DataLogImpl()
  : counter_(1),
    tables_(),
    flush_event_(EventWrapper::Create()),
    file_writer_thread_(NULL),
    tables_lock_(RWLockWrapper::CreateRWLock()) {
}

DataLogImpl::~DataLogImpl() {
  StopThread();
  Flush();  // Write any remaining rows
  delete file_writer_thread_;
  delete flush_event_;
  for (TableMap::iterator it = tables_.begin(); it != tables_.end();) {
    delete static_cast<LogTable*>(it->second);
    // For maps all iterators (except the erased) are valid after an erase
    tables_.erase(it++);
  }
  delete tables_lock_;
}

int DataLogImpl::CreateLog() {
  CriticalSectionScoped synchronize(crit_sect_);
  if (instance_ == NULL) {
    instance_ = new DataLogImpl();
    return instance_->Init();
  } else {
    ++instance_->counter_;
  }
  return 0;
}

int DataLogImpl::Init() {
  file_writer_thread_ = ThreadWrapper::CreateThread(
                          DataLogImpl::Run,
                          instance_,
                          kHighestPriority,
                          "DataLog");
  if (file_writer_thread_ == NULL)
    return -1;
  unsigned int thread_id = 0;
  bool success = file_writer_thread_->Start(thread_id);
  if (!success)
    return -1;
  return 0;
}

DataLogImpl* DataLogImpl::StaticInstance() {
  return instance_;
}

void DataLogImpl::ReturnLog() {
  CriticalSectionScoped synchronize(crit_sect_);
  if (instance_ && instance_->counter_ > 1) {
    --instance_->counter_;
    return;
  }
  delete instance_;
  instance_ = NULL;
}

int DataLogImpl::AddTable(const std::string& table_name) {
  WriteLockScoped synchronize(*tables_lock_);
  // Make sure we don't add a table which already exists
  if (tables_.count(table_name) > 0)
    return -1;
  tables_[table_name] = new LogTable();
  if (tables_[table_name]->CreateLogFile(table_name + ".txt") == -1)
    return -1;
  return 0;
}

int DataLogImpl::AddColumn(const std::string& table_name,
                           const std::string& column_name,
                           int multi_value_length) {
  ReadLockScoped synchronize(*tables_lock_);
  if (tables_.count(table_name) == 0)
    return -1;
  return tables_[table_name]->AddColumn(column_name, multi_value_length);
}

int DataLogImpl::InsertCell(const std::string& table_name,
                            const std::string& column_name,
                            const Container* value_container) {
  ReadLockScoped synchronize(*tables_lock_);
  assert(tables_.count(table_name) > 0);
  if (tables_.count(table_name) == 0)
    return -1;
  return tables_[table_name]->InsertCell(column_name, value_container);
}

int DataLogImpl::NextRow(const std::string& table_name) {
  ReadLockScoped synchronize(*tables_lock_);
  if (tables_.count(table_name) == 0)
    return -1;
  tables_[table_name]->NextRow();
  if (file_writer_thread_ == NULL) {
    // Write every row to file as they get complete.
    tables_[table_name]->Flush();
  } else {
    // Signal a complete row
    flush_event_->Set();
  }
  return 0;
}

void DataLogImpl::Flush() {
  ReadLockScoped synchronize(*tables_lock_);
  for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) {
    it->second->Flush();
  }
}

bool DataLogImpl::Run(void* obj) {
  static_cast<DataLogImpl*>(obj)->Process();
  return true;
}

void DataLogImpl::Process() {
  // Wait for a row to be complete
  flush_event_->Wait(WEBRTC_EVENT_INFINITE);
  Flush();
}

void DataLogImpl::StopThread() {
  if (file_writer_thread_ != NULL) {
    file_writer_thread_->SetNotAlive();
    flush_event_->Set();
    // Call Stop() repeatedly, waiting for the Flush() call in Process() to
    // finish.
    while (!file_writer_thread_->Stop()) continue;
  }
}

}  // namespace webrtc
<commit_msg>Switch to new critsect interface for DataLog.<commit_after>/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "data_log.h"

#include <assert.h>

#include <algorithm>
#include <list>

#include "critical_section_wrapper.h"
#include "event_wrapper.h"
#include "file_wrapper.h"
#include "rw_lock_wrapper.h"
#include "thread_wrapper.h"

namespace webrtc {

DataLogImpl::CritSectScopedPtr DataLogImpl::crit_sect_(
  CriticalSectionWrapper::CreateCriticalSection());

DataLogImpl* DataLogImpl::instance_ = NULL;

// A Row contains cells, which are indexed by the column names as std::string.
// The string index is treated in a case sensitive way.
class Row {
 public:
  Row();
  ~Row();

  // Inserts a Container into the cell of the column specified with
  // column_name.
  // column_name is treated in a case sensitive way.
  int InsertCell(const std::string& column_name,
                 const Container* value_container);

  // Converts the value at the column specified by column_name to a string
  // stored in value_string.
  // column_name is treated in a case sensitive way.
  void ToString(const std::string& column_name, std::string* value_string);

 private:
  // Collection of containers indexed by column name as std::string
  typedef std::map<std::string, const Container*> CellMap;

  CellMap                   cells_;
  CriticalSectionWrapper*   cells_lock_;
};

// A LogTable contains multiple rows, where only the latest row is active for
// editing. The rows are defined by the ColumnMap, which contains the name of
// each column and the length of the column (1 for one-value-columns and greater
// than 1 for multi-value-columns).
class LogTable {
 public:
  LogTable();
  ~LogTable();

  // Adds the column with name column_name to the table. The column will be a
  // multi-value-column if multi_value_length is greater than 1.
  // column_name is treated in a case sensitive way.
  int AddColumn(const std::string& column_name, int multi_value_length);

  // Buffers the current row while it is waiting to be written to file,
  // which is done by a call to Flush(). A new row is available when the
  // function returns
  void NextRow();

  // Inserts a Container into the cell of the column specified with
  // column_name.
  // column_name is treated in a case sensitive way.
  int InsertCell(const std::string& column_name,
                 const Container* value_container);

  // Creates a log file, named as specified in the string file_name, to
  // where the table will be written when calling Flush().
  int CreateLogFile(const std::string& file_name);

  // Write all complete rows to file.
  // May not be called by two threads simultaneously (doing so may result in
  // a race condition). Will be called by the file_writer_thread_ when that
  // thread is running.
  void Flush();

 private:
  // Collection of multi_value_lengths indexed by column name as std::string
  typedef std::map<std::string, int> ColumnMap;
  typedef std::list<Row*> RowList;

  ColumnMap               columns_;
  RowList                 rows_[2];
  RowList*                rows_history_;
  RowList*                rows_flush_;
  Row*                    current_row_;
  FileWrapper*            file_;
  bool                    write_header_;
  CriticalSectionWrapper* table_lock_;
};

Row::Row()
  : cells_(),
    cells_lock_(CriticalSectionWrapper::CreateCriticalSection()) {
}

Row::~Row() {
  for (CellMap::iterator it = cells_.begin(); it != cells_.end();) {
    delete it->second;
    // For maps all iterators (except the erased) are valid after an erase
    cells_.erase(it++);
  }
  delete cells_lock_;
}

int Row::InsertCell(const std::string& column_name,
                    const Container* value_container) {
  CriticalSectionScoped synchronize(cells_lock_);
  assert(cells_.count(column_name) == 0);
  if (cells_.count(column_name) > 0)
    return -1;
  cells_[column_name] = value_container;
  return 0;
}

void Row::ToString(const std::string& column_name,
                   std::string* value_string) {
  CriticalSectionScoped synchronize(cells_lock_);
  const Container* container = cells_[column_name];
  if (container == NULL) {
    *value_string = "NaN,";
    return;
  }
  container->ToString(value_string);
}

LogTable::LogTable()
  : columns_(),
    rows_(),
    rows_history_(&rows_[0]),
    rows_flush_(&rows_[1]),
    current_row_(new Row),
    file_(FileWrapper::Create()),
    write_header_(true),
    table_lock_(CriticalSectionWrapper::CreateCriticalSection()) {
}

LogTable::~LogTable() {
  for (RowList::iterator row_it = rows_history_->begin();
       row_it != rows_history_->end();) {
    delete *row_it;
    row_it = rows_history_->erase(row_it);
  }
  for (ColumnMap::iterator col_it = columns_.begin();
       col_it != columns_.end();) {
    // For maps all iterators (except the erased) are valid after an erase
    columns_.erase(col_it++);
  }
  if (file_ != NULL) {
    file_->Flush();
    file_->CloseFile();
    delete file_;
  }
  delete current_row_;
  delete table_lock_;
}

int LogTable::AddColumn(const std::string& column_name,
                        int multi_value_length) {
  assert(multi_value_length > 0);
  if (!write_header_) {
    // It's not allowed to add new columns after the header
    // has been written.
    assert(false);
    return -1;
  } else {
    CriticalSectionScoped synchronize(table_lock_);
    if (write_header_)
      columns_[column_name] = multi_value_length;
    else
      return -1;
  }
  return 0;
}

void LogTable::NextRow() {
  CriticalSectionScoped sync_rows(table_lock_);
  rows_history_->push_back(current_row_);
  current_row_ = new Row;
}

int LogTable::InsertCell(const std::string& column_name,
                         const Container* value_container) {
  CriticalSectionScoped synchronize(table_lock_);
  assert(columns_.count(column_name) > 0);
  if (columns_.count(column_name) == 0)
    return -1;
  return current_row_->InsertCell(column_name, value_container);
}

int LogTable::CreateLogFile(const std::string& file_name) {
  if (file_name.length() == 0)
    return -1;
  if (file_->Open())
    return -1;
  file_->OpenFile(file_name.c_str(),
                  false,  // Open with read/write permissions
                  false,  // Don't wraparound and write at the beginning when
                          // the file is full
                  true);  // Open as a text file
  if (file_ == NULL)
    return -1;
  return 0;
}

void LogTable::Flush() {
  ColumnMap::iterator column_it;
  bool commit_header = false;
  if (write_header_) {
    CriticalSectionScoped synchronize(table_lock_);
    if (write_header_) {
      commit_header = true;
      write_header_ = false;
    }
  }
  if (commit_header) {
    for (column_it = columns_.begin();
         column_it != columns_.end(); ++column_it) {
      if (column_it->second > 1) {
        file_->WriteText("%s[%u],", column_it->first.c_str(),
                         column_it->second);
        for (int i = 1; i < column_it->second; ++i)
          file_->WriteText(",");
      } else {
        file_->WriteText("%s,", column_it->first.c_str());
      }
    }
    if (columns_.size() > 0)
      file_->WriteText("\n");
  }

  // Swap the list used for flushing with the list containing the row history
  // and clear the history. We also create a local pointer to the new
  // list used for flushing to avoid race conditions if another thread
  // calls this function while we are writing.
  // We don't want to block the list while we're writing to file.
  {
    CriticalSectionScoped synchronize(table_lock_);
    RowList* tmp = rows_flush_;
    rows_flush_ = rows_history_;
    rows_history_ = tmp;
    rows_history_->clear();
  }

  // Write all complete rows to file and delete them
  for (RowList::iterator row_it = rows_flush_->begin();
       row_it != rows_flush_->end();) {
    for (column_it = columns_.begin();
         column_it != columns_.end(); ++column_it) {
      std::string row_string;
      (*row_it)->ToString(column_it->first, &row_string);
      file_->WriteText("%s", row_string.c_str());
    }
    if (columns_.size() > 0)
      file_->WriteText("\n");
    delete *row_it;
    row_it = rows_flush_->erase(row_it);
  }
}

int DataLog::CreateLog() {
  return DataLogImpl::CreateLog();
}

void DataLog::ReturnLog() {
  return DataLogImpl::ReturnLog();
}

std::string DataLog::Combine(const std::string& table_name, int table_id) {
  std::stringstream ss;
  std::string combined_id = table_name;
  std::string number_suffix;
  ss << "_" << table_id;
  ss >> number_suffix;
  combined_id += number_suffix;
  std::transform(combined_id.begin(), combined_id.end(), combined_id.begin(),
                 ::tolower);
  return combined_id;
}

int DataLog::AddTable(const std::string& table_name) {
  DataLogImpl* data_log = DataLogImpl::StaticInstance();
  if (data_log == NULL)
    return -1;
  return data_log->AddTable(table_name);
}

int DataLog::AddColumn(const std::string& table_name,
                       const std::string& column_name,
                       int multi_value_length) {
  DataLogImpl* data_log = DataLogImpl::StaticInstance();
  if (data_log == NULL)
    return -1;
  return data_log->DataLogImpl::StaticInstance()->AddColumn(table_name,
                                                            column_name,
                                                            multi_value_length);
}

int DataLog::NextRow(const std::string& table_name) {
  DataLogImpl* data_log = DataLogImpl::StaticInstance();
  if (data_log == NULL)
    return -1;
  return data_log->DataLogImpl::StaticInstance()->NextRow(table_name);
}

DataLogImpl::DataLogImpl()
  : counter_(1),
    tables_(),
    flush_event_(EventWrapper::Create()),
    file_writer_thread_(NULL),
    tables_lock_(RWLockWrapper::CreateRWLock()) {
}

DataLogImpl::~DataLogImpl() {
  StopThread();
  Flush();  // Write any remaining rows
  delete file_writer_thread_;
  delete flush_event_;
  for (TableMap::iterator it = tables_.begin(); it != tables_.end();) {
    delete static_cast<LogTable*>(it->second);
    // For maps all iterators (except the erased) are valid after an erase
    tables_.erase(it++);
  }
  delete tables_lock_;
}

int DataLogImpl::CreateLog() {
  CriticalSectionScoped synchronize(crit_sect_.get());
  if (instance_ == NULL) {
    instance_ = new DataLogImpl();
    return instance_->Init();
  } else {
    ++instance_->counter_;
  }
  return 0;
}

int DataLogImpl::Init() {
  file_writer_thread_ = ThreadWrapper::CreateThread(
                          DataLogImpl::Run,
                          instance_,
                          kHighestPriority,
                          "DataLog");
  if (file_writer_thread_ == NULL)
    return -1;
  unsigned int thread_id = 0;
  bool success = file_writer_thread_->Start(thread_id);
  if (!success)
    return -1;
  return 0;
}

DataLogImpl* DataLogImpl::StaticInstance() {
  return instance_;
}

void DataLogImpl::ReturnLog() {
  CriticalSectionScoped synchronize(crit_sect_.get());
  if (instance_ && instance_->counter_ > 1) {
    --instance_->counter_;
    return;
  }
  delete instance_;
  instance_ = NULL;
}

int DataLogImpl::AddTable(const std::string& table_name) {
  WriteLockScoped synchronize(*tables_lock_);
  // Make sure we don't add a table which already exists
  if (tables_.count(table_name) > 0)
    return -1;
  tables_[table_name] = new LogTable();
  if (tables_[table_name]->CreateLogFile(table_name + ".txt") == -1)
    return -1;
  return 0;
}

int DataLogImpl::AddColumn(const std::string& table_name,
                           const std::string& column_name,
                           int multi_value_length) {
  ReadLockScoped synchronize(*tables_lock_);
  if (tables_.count(table_name) == 0)
    return -1;
  return tables_[table_name]->AddColumn(column_name, multi_value_length);
}

int DataLogImpl::InsertCell(const std::string& table_name,
                            const std::string& column_name,
                            const Container* value_container) {
  ReadLockScoped synchronize(*tables_lock_);
  assert(tables_.count(table_name) > 0);
  if (tables_.count(table_name) == 0)
    return -1;
  return tables_[table_name]->InsertCell(column_name, value_container);
}

int DataLogImpl::NextRow(const std::string& table_name) {
  ReadLockScoped synchronize(*tables_lock_);
  if (tables_.count(table_name) == 0)
    return -1;
  tables_[table_name]->NextRow();
  if (file_writer_thread_ == NULL) {
    // Write every row to file as they get complete.
    tables_[table_name]->Flush();
  } else {
    // Signal a complete row
    flush_event_->Set();
  }
  return 0;
}

void DataLogImpl::Flush() {
  ReadLockScoped synchronize(*tables_lock_);
  for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) {
    it->second->Flush();
  }
}

bool DataLogImpl::Run(void* obj) {
  static_cast<DataLogImpl*>(obj)->Process();
  return true;
}

void DataLogImpl::Process() {
  // Wait for a row to be complete
  flush_event_->Wait(WEBRTC_EVENT_INFINITE);
  Flush();
}

void DataLogImpl::StopThread() {
  if (file_writer_thread_ != NULL) {
    file_writer_thread_->SetNotAlive();
    flush_event_->Set();
    // Call Stop() repeatedly, waiting for the Flush() call in Process() to
    // finish.
    while (!file_writer_thread_->Stop()) continue;
  }
}

}  // namespace webrtc
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qsystemalignedtimer.h"
#include "qsysteminfocommon_p.h"

QTM_BEGIN_NAMESPACE

/*!
  \class QSystemAlignedTimer
  \ingroup systeminfo
  \inmodule QtSystemInfo
  \brief The QSystemAlignedTimer class provides a service for applications to synchronize their activity.

  QSystemAlignedTimer is a fuzzy timer that allows applications that must do periodic activity like
  after being in sleep mode a certain period, to synchronize their activities in the same window of time.

  For example send network "alive" messages at the same time (i.e. turn the wireless radio on at the same time).

  The service is not only for network-aware applications, it is for use by any applications
  that need to periodic wake-ups.

  The recommended use case is when app uses single-shot timer only: set mintime 0 for the first call
  'to jump to the train' and mintime > 0 after 1st wakeup.

*/

/*!
  \fn void QSystemAlignedTimer::timeout()

  This signal is emitted when the timer times out.
 */

/*!
    \enum QSystemAlignedTimer::AlignedTimerError
    This enum describes the last known AlignedTimerError

    \value NoError                        No error.
    \value AlignedTimerNotSupported       The aligned timer is not support on this platform
    \value InvalidArgument                Interval arguments are invalid.
    \value TimerFailed                    General timer failure.
    \value InternalError                  Internal error.

  */

/*!
    \enum QSystemAlignedTimer::AlignedTimerError
    This enum describes the last known AlignedTimerError

    \value AlignedTimerNotSupported       The aligned timer is not support on this platform
    \value InvalidArgument                Interval arguments are invalid.
    \value TimerFailed                    General timer failure.

  */
/*!
   Constructs a QSystemAlignedTimer object with the given \a parent.
  */
QSystemAlignedTimer::QSystemAlignedTimer(QObject *parent)
    : QObject(parent)
{
    d = new QSystemAlignedTimerPrivate(parent);
    connect(d, SIGNAL(timeout()), this, SIGNAL(timeout()));
    connect(d, SIGNAL(error(QSystemAlignedTimer::AlignedTimerError)), this, SIGNAL(error(QSystemAlignedTimer::AlignedTimerError)));
}

/*!
    Destructs the QSystemAlignedTimer
  */
QSystemAlignedTimer::~QSystemAlignedTimer()
{
    delete d, d = 0;
}

/*!
  Starts the timer with the preferred interval of \a sec in seconds.

  This is not a guaranteed interval, and the timeout signal may be fired at any time,
  depending on other clients attached to this timer.

  In the case of 0, it means 'wake me up when someone else is woken up'.

  If you need a window of time in which your timer should fire, use QSystemAlignedTimer::setWindow

  */
void QSystemAlignedTimer::start(int minimumTime, int maximumTime)
{
    if (minimumTime > maximumTime || maximumTime <= 0) {
        emit error(QSystemAlignedTimer::InvalidArgument);
        return;
    }
    d->start(minimumTime, maximumTime);
}

/*!
    Starts the alignedtimer.
*/
void QSystemAlignedTimer::start()
{
    int minimumTime = minimumInterval();
    int maximumTime = maximumInterval();

    if (minimumTime > maximumTime || maximumTime <= 0) {
        emit error(QSystemAlignedTimer::InvalidArgument);
        return;
    }
    d->start();
}

/*!
     This should be called when the application wakes up via other means than QSystemAlignedTimer timeout.

    Other applications that are in their wakeup window *may* be woken up. Single-shot timer is canceled,
    and reoccuring timer interval will restart.

    Symbian does not support this wokeUp call for reoccuring timers and will simply ignore it.
  */
void QSystemAlignedTimer::wokeUp()
{
    d->wokeUp();
}

/*!
  Stops this timer request.
  */
void QSystemAlignedTimer::stop()
{
    d->stop();
}

/*!
  Set the timeout minimum interval to \a seconds in seconds that must be waited before timeout
  signal is emitted.

   Time in seconds that MUST be waited before timeout.
   Value 0 means 'wake me up when someboy else is woken'.

   mintime value 0 should be used with special care, as it may cause too frequent wakeups.
   It is recommended that the first wait (if possible) uses minimum value of 0 to
   "jump to the train" and minimum value > 0 after 1st wakeup

 */
void QSystemAlignedTimer::setMinimumInterval(int seconds)
{
    d->setMinimumInterval(seconds);
}

/*!
  \property QSystemAlignedTimer::minimumInterval
  \brief The timers's minimumInterval.

  Returns this current timer minimum interval.
  */
int QSystemAlignedTimer::minimumInterval() const
{
    return d->minimumInterval();
}

/*!
  Set the timeout maximum interval to \a seconds the wait must end.

   Time in seconds when the wait MUST end. It is wise to have maxtime-mintime
   quite big so all users of this service get synced.
   For example if you preferred wait is 120 seconds, use minval 110 and maxval 130.

   max interval must be greater than min interval.

 */
void QSystemAlignedTimer::setMaximumInterval(int seconds)
{
    d->setMaximumInterval(seconds);
}

/*!
  \property QSystemAlignedTimer::maximumInterval
  \brief The timers's maximumInterval.

  Returns this current timer maximum interval.
  */
int QSystemAlignedTimer::maximumInterval() const
{
    return d->maximumInterval();
}

/*!
  Sets this timer to be a single shot \a singleShot is true, otherwise false.
  */
inline void QSystemAlignedTimer::setSingleShot(bool singleShot)
{
    d->setSingleShot(singleShot);
}

/*!

  This static function calls a slot after a given time interval.
  \a sec is the time interval in seconds.
  The receiver is the \a receiver object and the \a member is the slot.
  */
void QSystemAlignedTimer::singleShot(int minimumTime, int maximumTime, QObject *receiver, const char *member)
{
    if (minimumTime > maximumTime || maximumTime <= 0) {
        return;
    }
    QSystemAlignedTimerPrivate::singleShot(minimumTime, maximumTime, receiver, member);
}

/*!
  \property QSystemAlignedTimer::singleShot
  Whether the timer is single shot.
*/

/*!
  Returns true if this timer is a single shot, otherwise false.
  */
bool QSystemAlignedTimer::isSingleShot() const
{
    return d->isSingleShot();
}

/*!
  Returns the last AlignedTimerError.

  */
QSystemAlignedTimer::AlignedTimerError QSystemAlignedTimer::lastError() const
{
    return d->lastError();
}

/*!
    \property QSystemAlignedTimer::active
  Returns true if the timer is running; otherwise false.
*/

/*!
  Returns true if the timer is running; otherwise false.
*/
bool	QSystemAlignedTimer::isActive () const
{
    return d->isActive();
}

#include "moc_qsystemalignedtimer.cpp"

QTM_END_NAMESPACE
<commit_msg>mac needs to use Q_EMIT macro<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qsystemalignedtimer.h"
#include "qsysteminfocommon_p.h"

QTM_BEGIN_NAMESPACE

/*!
  \class QSystemAlignedTimer
  \ingroup systeminfo
  \inmodule QtSystemInfo
  \brief The QSystemAlignedTimer class provides a service for applications to synchronize their activity.

  QSystemAlignedTimer is a fuzzy timer that allows applications that must do periodic activity like
  after being in sleep mode a certain period, to synchronize their activities in the same window of time.

  For example send network "alive" messages at the same time (i.e. turn the wireless radio on at the same time).

  The service is not only for network-aware applications, it is for use by any applications
  that need to periodic wake-ups.

  The recommended use case is when app uses single-shot timer only: set mintime 0 for the first call
  'to jump to the train' and mintime > 0 after 1st wakeup.

*/

/*!
  \fn void QSystemAlignedTimer::timeout()

  This signal is emitted when the timer times out.
 */

/*!
    \enum QSystemAlignedTimer::AlignedTimerError
    This enum describes the last known AlignedTimerError

    \value NoError                        No error.
    \value AlignedTimerNotSupported       The aligned timer is not support on this platform
    \value InvalidArgument                Interval arguments are invalid.
    \value TimerFailed                    General timer failure.
    \value InternalError                  Internal error.

  */

/*!
    \enum QSystemAlignedTimer::AlignedTimerError
    This enum describes the last known AlignedTimerError

    \value AlignedTimerNotSupported       The aligned timer is not support on this platform
    \value InvalidArgument                Interval arguments are invalid.
    \value TimerFailed                    General timer failure.

  */
/*!
   Constructs a QSystemAlignedTimer object with the given \a parent.
  */
QSystemAlignedTimer::QSystemAlignedTimer(QObject *parent)
    : QObject(parent)
{
    d = new QSystemAlignedTimerPrivate(parent);
    connect(d, SIGNAL(timeout()), this, SIGNAL(timeout()));
    connect(d, SIGNAL(error(QSystemAlignedTimer::AlignedTimerError)), this, SIGNAL(error(QSystemAlignedTimer::AlignedTimerError)));
}

/*!
    Destructs the QSystemAlignedTimer
  */
QSystemAlignedTimer::~QSystemAlignedTimer()
{
    delete d, d = 0;
}

/*!
  Starts the timer with the preferred interval of \a sec in seconds.

  This is not a guaranteed interval, and the timeout signal may be fired at any time,
  depending on other clients attached to this timer.

  In the case of 0, it means 'wake me up when someone else is woken up'.

  If you need a window of time in which your timer should fire, use QSystemAlignedTimer::setWindow

  */
void QSystemAlignedTimer::start(int minimumTime, int maximumTime)
{
    if (minimumTime > maximumTime || maximumTime <= 0) {
        Q_EMIT error(QSystemAlignedTimer::InvalidArgument);
        return;
    }
    d->start(minimumTime, maximumTime);
}

/*!
    Starts the alignedtimer.
*/
void QSystemAlignedTimer::start()
{
    int minimumTime = minimumInterval();
    int maximumTime = maximumInterval();

    if (minimumTime > maximumTime || maximumTime <= 0) {
        Q_EMIT error(QSystemAlignedTimer::InvalidArgument);
        return;
    }
    d->start();
}

/*!
     This should be called when the application wakes up via other means than QSystemAlignedTimer timeout.

    Other applications that are in their wakeup window *may* be woken up. Single-shot timer is canceled,
    and reoccuring timer interval will restart.

    Symbian does not support this wokeUp call for reoccuring timers and will simply ignore it.
  */
void QSystemAlignedTimer::wokeUp()
{
    d->wokeUp();
}

/*!
  Stops this timer request.
  */
void QSystemAlignedTimer::stop()
{
    d->stop();
}

/*!
  Set the timeout minimum interval to \a seconds in seconds that must be waited before timeout
  signal is emitted.

   Time in seconds that MUST be waited before timeout.
   Value 0 means 'wake me up when someboy else is woken'.

   mintime value 0 should be used with special care, as it may cause too frequent wakeups.
   It is recommended that the first wait (if possible) uses minimum value of 0 to
   "jump to the train" and minimum value > 0 after 1st wakeup

 */
void QSystemAlignedTimer::setMinimumInterval(int seconds)
{
    d->setMinimumInterval(seconds);
}

/*!
  \property QSystemAlignedTimer::minimumInterval
  \brief The timers's minimumInterval.

  Returns this current timer minimum interval.
  */
int QSystemAlignedTimer::minimumInterval() const
{
    return d->minimumInterval();
}

/*!
  Set the timeout maximum interval to \a seconds the wait must end.

   Time in seconds when the wait MUST end. It is wise to have maxtime-mintime
   quite big so all users of this service get synced.
   For example if you preferred wait is 120 seconds, use minval 110 and maxval 130.

   max interval must be greater than min interval.

 */
void QSystemAlignedTimer::setMaximumInterval(int seconds)
{
    d->setMaximumInterval(seconds);
}

/*!
  \property QSystemAlignedTimer::maximumInterval
  \brief The timers's maximumInterval.

  Returns this current timer maximum interval.
  */
int QSystemAlignedTimer::maximumInterval() const
{
    return d->maximumInterval();
}

/*!
  Sets this timer to be a single shot \a singleShot is true, otherwise false.
  */
inline void QSystemAlignedTimer::setSingleShot(bool singleShot)
{
    d->setSingleShot(singleShot);
}

/*!

  This static function calls a slot after a given time interval.
  \a sec is the time interval in seconds.
  The receiver is the \a receiver object and the \a member is the slot.
  */
void QSystemAlignedTimer::singleShot(int minimumTime, int maximumTime, QObject *receiver, const char *member)
{
    if (minimumTime > maximumTime || maximumTime <= 0) {
        return;
    }
    QSystemAlignedTimerPrivate::singleShot(minimumTime, maximumTime, receiver, member);
}

/*!
  \property QSystemAlignedTimer::singleShot
  Whether the timer is single shot.
*/

/*!
  Returns true if this timer is a single shot, otherwise false.
  */
bool QSystemAlignedTimer::isSingleShot() const
{
    return d->isSingleShot();
}

/*!
  Returns the last AlignedTimerError.

  */
QSystemAlignedTimer::AlignedTimerError QSystemAlignedTimer::lastError() const
{
    return d->lastError();
}

/*!
    \property QSystemAlignedTimer::active
  Returns true if the timer is running; otherwise false.
*/

/*!
  Returns true if the timer is running; otherwise false.
*/
bool	QSystemAlignedTimer::isActive () const
{
    return d->isActive();
}

#include "moc_qsystemalignedtimer.cpp"

QTM_END_NAMESPACE
<|endoftext|>


<commit_before>/***********************************************************************************

    Copyright (C) 2013 by Sajia Akhter, Edwards Lab, San Diego State University

    This file is part of Qudaich.

    Qudaich is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

**********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h> /* required for MAX_PATH */

void version(char *);
char *execpath(char *);
void inputError(const char *);
void help(char *);

int TOTAL_DB_SEQ = 0, TOTAL_QRY_SEQ = 0, LINE_LEN, SIZE_SEQ = 0, SIZE_SEQ_NAME = 0;
char mypath[PATH_MAX];

void inputError(const char *msg) {
  fprintf(stderr, "%s", msg);
  exit(-1);
}

/* return the path of the current executable so we can append bin to it
 * arg: exec is argv[0]
 * returns: a string representing the current path
 *
 * Uses realpath in stdlib.h and MAX_PATH in limits.h
 */
char *execpath(char *exec) {
	char *mp = realpath(exec, mypath); /*  this is the complete path */
	char *ptr = strrchr(mp, '/'); /* this is from the last / forwards - should be path separator! */
	int posn = strlen(mp) - strlen(ptr); /* the position of the last / */
	mypath[posn] = '\0';
	return mypath;
}

/* print out the version number and then the help menu. Note that we require argv[0] so we can pass it forward */
void version(char *p) {
	FILE * fp;
	char v[256];
	fp = fopen("VERSION", "r");
	if (fp != NULL) {
		char *check_fgets = fgets(v, 256, fp);
	}
	printf("\n%s %s\n", p, v);
	help(p);
}

/* help() prints the help menu and exits. Please provide argv[0] as an option so we can include that in the help menu */
void help(char * p) {
      printf("\n%s -q [query] -d [database] -p [program] or\n%s -query [query file] -db [database file] -prog [program]\n\n", p, p);
      printf("-q|-query                      Name of the query file (Required)\n");
      printf("-d|-db                         Name of database file (Required)\n");
      printf("-p|-prog                       Alignment options (Required):  n (nucleotide),\n");
      printf("                                                           p (protein),\n");
      printf("                                                           trn (translated nucleotide)\n");
      printf("                                                           trnx (translated nucleotide vs protein database)\n");
      printf("-t|-top                        Number of alignments per query sequence (default 1)\n");
      printf("-f|-freqFile                   Frequency file Name\n");
      printf("-c|-heuristic                  Heuristic options: 1 (default) or 2 (See the README or paper for more information)\n");
      printf("-h|-help                       Show command line options and exit\n");
      printf("-v|-version                    Print the version and help options and exit\n");
      exit(0);
}

int input_size(FILE *f, int flag)
{
  int L = 2000, SIZE = 0, t, max = 0, total_seq = 0, temp = 0, maxsize=0 ;
  char name[L];

  while(fgets(name,L,f)!=NULL)
  {
        if (name[0] == '>'){
		total_seq++;
		t = strlen(name);
		if(t>max) max = t;
		SIZE += temp;
		if (temp >  maxsize) maxsize = temp;
		temp = 0;
                continue;
	}
        else
	{
		t = strlen(name)-1;
		if((name[t]) != '\n')
            		t++;
                temp = temp + t;
	}
  }
  SIZE += temp;
  if (temp >  maxsize) maxsize = temp;

  if (flag == 0){//DB
	TOTAL_DB_SEQ = total_seq;
        if (SIZE_SEQ<maxsize) SIZE_SEQ = maxsize;
	if (SIZE_SEQ_NAME<max) SIZE_SEQ_NAME = max;
  }
  else//query
  {
 	TOTAL_QRY_SEQ = total_seq;
        if (SIZE_SEQ<maxsize) SIZE_SEQ = maxsize;
        if (SIZE_SEQ_NAME<max) SIZE_SEQ_NAME = max;
  }
  return SIZE;
}

int main(int argc, char **argv) {
  int i;
  char queryFile[1024], refFile[1024], outputfile[1024], program[16];
  int top = 1, hypothesis = 1;
  FILE *f;

  strcpy(outputfile, "freq.txt");

  for(i=1;i<argc;i++) {
    if(!strcmp(argv[i], "-query") || !strcmp(argv[i], "-q")) {
      if(i+1 >= argc) {
	inputError("Query File name required\n");
      }
      strcpy(queryFile, argv[i+1]);
      i++;
    }
    else if(!strcmp(argv[i], "-ref") || !strcmp(argv[i], "-db") || !strcmp(argv[i], "-d")) {
      if(i+1 >= argc) {
	inputError("Database File name required\n");
      }
      strcpy(refFile, argv[i+1]);
      i++;
    }
    else if(!strcmp(argv[i], "-prog") || !strcmp(argv[i], "-p")) {
      if(i+1 >= argc) {
        inputError("Alignment option required\n");
      }
      strcpy(program, argv[i+1]);
      if (strcmp(program,"n")!=0 and strcmp(program,"p")!=0 and strcmp(program,"trn")!=0 and strcmp(program,"trnx")!=0)
	inputError("Valid alignment option required\nPlease use -h for details information\n");
      i++;
    }

    else if(!strcmp(argv[i], "-freqFile") || !strcmp(argv[i], "-f")) {
      if(i+1 >= argc) {
        inputError("Frequency File name required\n");
      }
      strcpy(outputfile, argv[i+1]);
      i++;
    }
    else if(!strcmp(argv[i], "-hypo") || !strcmp(argv[i], "-heuristic") || !strcmp(argv[i], "-c")) {
      if(i+1 >= argc) {
        inputError("Argument required\n");
      }
      sscanf(argv[i+1], "%d", &hypothesis);
      if(hypothesis <= 0 || hypothesis >=3) {
        inputError("Value of the heuristic must be either 1 (default) or 2\n");
      }
      i++;
    }
    else if(!strcmp(argv[i], "-top") || (!strcmp(argv[i], "-t")) {
      if(i+1 >= argc) {
	inputError("Argument required\n");
      }
      sscanf(argv[i+1], "%d", &top);
      if(top <= 0) {
	inputError("Value of top must be positive\n");
      }
      i++;
    }
    else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help"))
	    help(argv[0]);
    else if (!strcmp(argv[i], "-version") || !strcmp(argv[i], "-v"))
	    version(argv[0]);
    else {
      inputError("Invalid arguments\n");
      inputError(argv[i]);
    }
  }

  if(!queryFile[0] || !refFile[0] || !program[0]) {
    help(argv[0]);
  }

  // CHECK whether these are valid files
  unsigned int SIZEr = 0, SIZEq = 0, SIZE = 0;

  //read file:
  f = fopen(refFile,"r");
  SIZEr = input_size(f,0);
  fclose(f);
  f = fopen(queryFile,"r");
  SIZEq = input_size(f,1);
  fclose(f);

  char progBuf[1024],write_buff[1024];
  LINE_LEN = 2000;

  SIZE_SEQ++;
  SIZE_SEQ_NAME++;
  sprintf(write_buff,"%d %d %d %d %d", TOTAL_DB_SEQ, TOTAL_QRY_SEQ, LINE_LEN, SIZE_SEQ, SIZE_SEQ_NAME);

  char *app_path;
  if ((app_path = execpath(argv[0])) == NULL) {
	  fprintf(stderr, "ERROR: We can not get an application path from %s\n", argv[0]);
	  exit(-1);
  }

  if (strcmp(program,"n")==0)
  {
	SIZE = SIZEr * 2 + SIZEq + 2;
        if (SIZE >= 2000000000){
		 printf("Current version of Quadich can handle maximum 2 billion bp.\nThe input database and query file contain %u bps [= total database bp (%u)  * 2 + total query bp (%u)]\n",SIZE,SIZEr,SIZEq);
		inputError("");
	}

	if(hypothesis == 1)
	{
		if(top == 1)
			sprintf(progBuf, "%s/bin/search_dna_hypo1 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
		else
			sprintf(progBuf, "%s/bin/search_dna_hypo1_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
	}
	else
	{
	        if(top == 1)
                        sprintf(progBuf, "%s/bin/search_dna_hypo2 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_dna_hypo2_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
	}
  }
  else if (strcmp(program,"p")==0)
  {
        SIZE = SIZEr + SIZEq + 2;
        if (SIZE >= 2000000000){
                          printf("Current version of Quadich can handle maximum 2 billion bp.\nThe input database and query file contain %u bps [= total database bp (%u) + total query bp (%u)]\n",SIZE,SIZEr,SIZEq);
                inputError("");
        }
	if(hypothesis == 1)
        {
                if(top == 1)
                        sprintf(progBuf, "%s/bin/search_pro_hypo1 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_pro_hypo1_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
        else
        {
                if(top == 1)
                        sprintf(progBuf, "%s/bin/search_pro_hypo2 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_pro_hypo2_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
  }
  else if (strcmp(program,"trn")==0)
  {
        SIZE = SIZEr * 2 + SIZEq + 2;
        if (SIZE >= 2000000000) {
                          printf("Current version of Quadich can handle maximum 2 billion bp.\nThe input database and query file contain %u bps [= total database bp (%u)  * 2 + total query bp (%u)]\n",SIZE,SIZEr,SIZEq);
                inputError("");
        }
	if(hypothesis == 1)
        {
                if(top == 1)
                        sprintf(progBuf, "%s/bin/search_trn_hypo1 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_trn_hypo1_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
        else
        {
                if(top == 1)
                        sprintf(progBuf, "%s/bin/search_trn_hypo2 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_trn_hypo2_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
  }

//////////////////
  else if (strcmp(program,"trnx")==0)
  {
        SIZE = SIZEr + SIZEq*2 + 2;
        if (SIZE >= 2000000000) {
                          printf("Current version of Quadich can handle maximum 2 billion bp.\nThe input database and query file contain %u bps [= total database bp (%u) + total query bp (%u) * 2]\n",SIZE,SIZEr,SIZEq);
                inputError("");
        }
        if(hypothesis == 1)
        {
                        sprintf(progBuf, "%s/bin/search_trnx_hypo1_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
        else
        {
		printf("Current version of Quadich supports only hypothesis I for translated nucleotide vs protein database alignmnet.\n\n");
                inputError("");
                /*if(top == 1)
                        sprintf(progBuf, "%s/bin/search_trn_hypo2 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_trn_hypo2_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);

	*/
        }
  }
  else{
        inputError("Please specify the correct program option\n");
      }

  printf("%s\n",progBuf);
  int returnval = system(progBuf);

  return returnval;
}

<commit_msg>Fixing an install issue<commit_after>/***********************************************************************************

    Copyright (C) 2013 by Sajia Akhter, Edwards Lab, San Diego State University

    This file is part of Qudaich.

    Qudaich is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

**********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h> /* required for MAX_PATH */

void version(char *);
char *execpath(char *);
void inputError(const char *);
void help(char *);

int TOTAL_DB_SEQ = 0, TOTAL_QRY_SEQ = 0, LINE_LEN, SIZE_SEQ = 0, SIZE_SEQ_NAME = 0;
char mypath[PATH_MAX];

void inputError(const char *msg) {
  fprintf(stderr, "%s", msg);
  exit(-1);
}

/* return the path of the current executable so we can append bin to it
 * arg: exec is argv[0]
 * returns: a string representing the current path
 *
 * Uses realpath in stdlib.h and MAX_PATH in limits.h
 */
char *execpath(char *exec) {
	char *mp = realpath(exec, mypath); /*  this is the complete path */
	char *ptr = strrchr(mp, '/'); /* this is from the last / forwards - should be path separator! */
	int posn = strlen(mp) - strlen(ptr); /* the position of the last / */
	mypath[posn] = '\0';
	return mypath;
}

/* print out the version number and then the help menu. Note that we require argv[0] so we can pass it forward */
void version(char *p) {
	FILE * fp;
	char v[256];
	fp = fopen("VERSION", "r");
	if (fp != NULL) {
		char *check_fgets = fgets(v, 256, fp);
	}
	printf("\n%s %s\n", p, v);
	help(p);
}

/* help() prints the help menu and exits. Please provide argv[0] as an option so we can include that in the help menu */
void help(char * p) {
      printf("\n%s -q [query] -d [database] -p [program] or\n%s -query [query file] -db [database file] -prog [program]\n\n", p, p);
      printf("-q|-query                      Name of the query file (Required)\n");
      printf("-d|-db                         Name of database file (Required)\n");
      printf("-p|-prog                       Alignment options (Required):  n (nucleotide),\n");
      printf("                                                           p (protein),\n");
      printf("                                                           trn (translated nucleotide)\n");
      printf("                                                           trnx (translated nucleotide vs protein database)\n");
      printf("-t|-top                        Number of alignments per query sequence (default 1)\n");
      printf("-f|-freqFile                   Frequency file Name\n");
      printf("-c|-heuristic                  Heuristic options: 1 (default) or 2 (See the README or paper for more information)\n");
      printf("-h|-help                       Show command line options and exit\n");
      printf("-v|-version                    Print the version and help options and exit\n");
      exit(0);
}

int input_size(FILE *f, int flag)
{
  int L = 2000, SIZE = 0, t, max = 0, total_seq = 0, temp = 0, maxsize=0 ;
  char name[L];

  while(fgets(name,L,f)!=NULL)
  {
        if (name[0] == '>'){
		total_seq++;
		t = strlen(name);
		if(t>max) max = t;
		SIZE += temp;
		if (temp >  maxsize) maxsize = temp;
		temp = 0;
                continue;
	}
        else
	{
		t = strlen(name)-1;
		if((name[t]) != '\n')
            		t++;
                temp = temp + t;
	}
  }
  SIZE += temp;
  if (temp >  maxsize) maxsize = temp;

  if (flag == 0){//DB
	TOTAL_DB_SEQ = total_seq;
        if (SIZE_SEQ<maxsize) SIZE_SEQ = maxsize;
	if (SIZE_SEQ_NAME<max) SIZE_SEQ_NAME = max;
  }
  else//query
  {
 	TOTAL_QRY_SEQ = total_seq;
        if (SIZE_SEQ<maxsize) SIZE_SEQ = maxsize;
        if (SIZE_SEQ_NAME<max) SIZE_SEQ_NAME = max;
  }
  return SIZE;
}

int main(int argc, char **argv) {
  int i;
  char queryFile[1024], refFile[1024], outputfile[1024], program[16];
  int top = 1, hypothesis = 1;
  FILE *f;

  strcpy(outputfile, "freq.txt");

  for(i=1;i<argc;i++) {
    if(!strcmp(argv[i], "-query") || !strcmp(argv[i], "-q")) {
      if(i+1 >= argc) {
	inputError("Query File name required\n");
      }
      strcpy(queryFile, argv[i+1]);
      i++;
    }
    else if(!strcmp(argv[i], "-ref") || !strcmp(argv[i], "-db") || !strcmp(argv[i], "-d")) {
      if(i+1 >= argc) {
	inputError("Database File name required\n");
      }
      strcpy(refFile, argv[i+1]);
      i++;
    }
    else if(!strcmp(argv[i], "-prog") || !strcmp(argv[i], "-p")) {
      if(i+1 >= argc) {
        inputError("Alignment option required\n");
      }
      strcpy(program, argv[i+1]);
      if (strcmp(program,"n")!=0 and strcmp(program,"p")!=0 and strcmp(program,"trn")!=0 and strcmp(program,"trnx")!=0)
	inputError("Valid alignment option required\nPlease use -h for details information\n");
      i++;
    }

    else if(!strcmp(argv[i], "-freqFile") || !strcmp(argv[i], "-f")) {
      if(i+1 >= argc) {
        inputError("Frequency File name required\n");
      }
      strcpy(outputfile, argv[i+1]);
      i++;
    }
    else if(!strcmp(argv[i], "-hypo") || !strcmp(argv[i], "-heuristic") || !strcmp(argv[i], "-c")) {
      if(i+1 >= argc) {
        inputError("Argument required\n");
      }
      sscanf(argv[i+1], "%d", &hypothesis);
      if(hypothesis <= 0 || hypothesis >=3) {
        inputError("Value of the heuristic must be either 1 (default) or 2\n");
      }
      i++;
    }
    else if(!strcmp(argv[i], "-top") || !strcmp(argv[i], "-t")) {
      if(i+1 >= argc) {
	inputError("Argument required\n");
      }
      sscanf(argv[i+1], "%d", &top);
      if(top <= 0) {
	inputError("Value of top must be positive\n");
      }
      i++;
    }
    else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help"))
	    help(argv[0]);
    else if (!strcmp(argv[i], "-version") || !strcmp(argv[i], "-v"))
	    version(argv[0]);
    else {
      inputError("Invalid arguments\n");
      inputError(argv[i]);
    }
  }

  if(!queryFile[0] || !refFile[0] || !program[0]) {
    help(argv[0]);
  }

  // CHECK whether these are valid files
  unsigned int SIZEr = 0, SIZEq = 0, SIZE = 0;

  //read file:
  f = fopen(refFile,"r");
  SIZEr = input_size(f,0);
  fclose(f);
  f = fopen(queryFile,"r");
  SIZEq = input_size(f,1);
  fclose(f);

  char progBuf[1024],write_buff[1024];
  LINE_LEN = 2000;

  SIZE_SEQ++;
  SIZE_SEQ_NAME++;
  sprintf(write_buff,"%d %d %d %d %d", TOTAL_DB_SEQ, TOTAL_QRY_SEQ, LINE_LEN, SIZE_SEQ, SIZE_SEQ_NAME);

  char *app_path;
  if ((app_path = execpath(argv[0])) == NULL) {
	  fprintf(stderr, "ERROR: We can not get an application path from %s\n", argv[0]);
	  exit(-1);
  }

  if (strcmp(program,"n")==0)
  {
	SIZE = SIZEr * 2 + SIZEq + 2;
        if (SIZE >= 2000000000){
		 printf("Current version of Quadich can handle maximum 2 billion bp.\nThe input database and query file contain %u bps [= total database bp (%u)  * 2 + total query bp (%u)]\n",SIZE,SIZEr,SIZEq);
		inputError("");
	}

	if(hypothesis == 1)
	{
		if(top == 1)
			sprintf(progBuf, "%s/bin/search_dna_hypo1 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
		else
			sprintf(progBuf, "%s/bin/search_dna_hypo1_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
	}
	else
	{
	        if(top == 1)
                        sprintf(progBuf, "%s/bin/search_dna_hypo2 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_dna_hypo2_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
	}
  }
  else if (strcmp(program,"p")==0)
  {
        SIZE = SIZEr + SIZEq + 2;
        if (SIZE >= 2000000000){
                          printf("Current version of Quadich can handle maximum 2 billion bp.\nThe input database and query file contain %u bps [= total database bp (%u) + total query bp (%u)]\n",SIZE,SIZEr,SIZEq);
                inputError("");
        }
	if(hypothesis == 1)
        {
                if(top == 1)
                        sprintf(progBuf, "%s/bin/search_pro_hypo1 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_pro_hypo1_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
        else
        {
                if(top == 1)
                        sprintf(progBuf, "%s/bin/search_pro_hypo2 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_pro_hypo2_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
  }
  else if (strcmp(program,"trn")==0)
  {
        SIZE = SIZEr * 2 + SIZEq + 2;
        if (SIZE >= 2000000000) {
                          printf("Current version of Quadich can handle maximum 2 billion bp.\nThe input database and query file contain %u bps [= total database bp (%u)  * 2 + total query bp (%u)]\n",SIZE,SIZEr,SIZEq);
                inputError("");
        }
	if(hypothesis == 1)
        {
                if(top == 1)
                        sprintf(progBuf, "%s/bin/search_trn_hypo1 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_trn_hypo1_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
        else
        {
                if(top == 1)
                        sprintf(progBuf, "%s/bin/search_trn_hypo2 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_trn_hypo2_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
  }

//////////////////
  else if (strcmp(program,"trnx")==0)
  {
        SIZE = SIZEr + SIZEq*2 + 2;
        if (SIZE >= 2000000000) {
                          printf("Current version of Quadich can handle maximum 2 billion bp.\nThe input database and query file contain %u bps [= total database bp (%u) + total query bp (%u) * 2]\n",SIZE,SIZEr,SIZEq);
                inputError("");
        }
        if(hypothesis == 1)
        {
                        sprintf(progBuf, "%s/bin/search_trnx_hypo1_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
        }
        else
        {
		printf("Current version of Quadich supports only hypothesis I for translated nucleotide vs protein database alignmnet.\n\n");
                inputError("");
                /*if(top == 1)
                        sprintf(progBuf, "%s/bin/search_trn_hypo2 %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);
                else
                        sprintf(progBuf, "%s/bin/search_trn_hypo2_top %s %s %s %d %d %s", app_path, refFile, queryFile, outputfile, SIZE, top, write_buff);

	*/
        }
  }
  else{
        inputError("Please specify the correct program option\n");
      }

  printf("%s\n",progBuf);
  int returnval = system(progBuf);

  return returnval;
}

<|endoftext|>


<commit_before>#include "Array.h"
#include "Runtime.h"
#include "SnowString.h"
#include "Function.h"
#include <sstream>

#define DEFAULT_ARRAY_LENGTH 8

namespace snow {
	GC_ROOTS_IMPL(Array) {
		GC_SUPER(Object);

		GC_ROOT(m_Data);
		for (size_t i = 0; i < m_Length; ++i) {
			GC_ROOT(m_Data[i]);
		}
	}

	void Array::resize(size_t new_size) {
		// TODO: Grow more than strictly necessary to minimize allocations
		VALUE* old_pointer = m_Data;
		m_Data = new(kGarbage, kBlob) VALUE[new_size];
		m_AllocatedSize = new_size;
		if (m_Length != 0 && old_pointer) {
			memcpy(m_Data, old_pointer, m_Length*sizeof(VALUE));
		}
	}

	Array::Array(size_t preallocate_length) : Object(array_prototype()), m_Data(NULL), m_Length(0), m_AllocatedSize(0) {
		if (preallocate_length == 0)
			preallocate_length = DEFAULT_ARRAY_LENGTH;
		resize(preallocate_length * sizeof(VALUE));
	}

	Array::Array(const Array& other) : Object(array_prototype()), m_Data(other.m_Data), m_Length(other.m_Length), m_AllocatedSize(0) {
		resize(m_Length * sizeof(VALUE));
	}

	Array::Array(VALUE* existing_array, size_t len, bool copy) : Object(array_prototype()), m_Data(existing_array), m_Length(len), m_AllocatedSize(0) {
		if (copy)
			resize(m_Length * sizeof(VALUE));
	}

	void Array::ensure_length(size_t len) {
		ASSERT(!is_frozen());
		if (len * sizeof(VALUE) <= m_AllocatedSize)
			return;
		resize(len * sizeof(VALUE));
	}

	VALUE& Array::operator[](int64_t idx) {
		if (idx < 0)
			idx %= m_Length;
		if (idx >= (int64_t)m_Length)
		{
			ensure_length(idx+1);
			auto old_length = m_Length;
			m_Length = idx+1;
			for (size_t i = old_length; i < m_Length; ++i) {
				m_Data[i] = nil();
			}
		}
		return m_Data[idx];
	}

	VALUE Array::operator[](int64_t idx) const {
		if (idx < 0)
			idx = m_Length + idx;
		if (idx < 0 || idx >= (int64_t)m_Length)
			return nil();
		return m_Data[idx];
	}

	VALUE Array::push(VALUE val) {
		ensure_length(m_Length + 1);
		m_Data[m_Length] = val;
		++m_Length;
		return val;
	}

	VALUE Array::pop() {
		ASSERT(!is_frozen());
		if (m_Length == 0)
			return nil();
		VALUE val = m_Data[m_Length-1];
		--m_Length;
		return val;
	}

	VALUE Array::unshift(VALUE val) {
		ensure_length(m_Length+1);
		// XXX: For some reason, memmove doesn't work here. Investigate!
		// Also, this might be acceptable, since it's faster than memmove.
		for (size_t i = 0; i < m_Length; ++i) {
			m_Data[m_Length-i] = m_Data[m_Length-1-i];
		}
		++m_Length;
		m_Data[0] = val;
		return val;
	}

	VALUE Array::shift() {
		ASSERT(!is_frozen());
		if (m_Length == 0)
			return nil();
		VALUE val = m_Data[0];
		--m_Length;
		for (size_t i = 0; i < m_Length; ++i) {
			m_Data[i] = m_Data[i+1];
		}
		return val;
	}

	VALUE Array::va_call(VALUE self, uint64_t num_args, va_list& args) {
		if (num_args == 0)
			return this;
		if (num_args == 1) {
			int64_t index = integer(va_arg(args, VALUE));
			return (*this)[index];
		}
		// TODO: ranges and such.
		TRAP();
		return this;
	}

	static VALUE array_new(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		return new Array(args, num_args);
	}

	static VALUE array_get(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		int64_t idx = integer(args[0]);
		return (*array)[idx];
	}

	static VALUE array_set(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 2);
		auto array = object_cast<Array>(self);
		int idx = integer(args[0]);
		VALUE new_value = args[1];
		return array->set_by_index(idx, new_value);
	}

	static VALUE array_each(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args >= 1);
		auto array = object_cast<Array>(self);

		VALUE closure = args[0];
		for (size_t i = 0; i < array->length(); ++i) {
			snow::call(NULL, closure, 2, (*array)[i], value((int64_t)i));
		}
		return self;
	}

	static VALUE array_length(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		auto array = object_cast<Array>(self);
		return value((int64_t)array->length());
	}

	static VALUE array_inspect(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		auto array = object_cast<Array>(self);
		std::stringstream ss;

		ss << "@(";
		for (size_t i = 0; i < array->length(); ++i) {
			ss << value_to_string(snow::call_method((*array)[i], "inspect", 0));
			if (i != array->length() - 1)
				ss << ", ";
		}
		ss << ")";

		return new String(ss.str());
	}

	static VALUE array_push(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		VALUE val = args[0];
		array->push(val);
		return self;
	}

	static VALUE array_pop(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 0);
		auto array = object_cast<Array>(self);
		return array->pop();
	}

	static VALUE array_unshift(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		VALUE val = args[0];
		return array->unshift(val);
	}

	static VALUE array_shift(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 0);
		auto array = object_cast<Array>(self);
		return array->shift();
	}

	Object* array_prototype() {
		static Object* proto = NULL;
		if (proto) return proto;
		proto = new(kMalloc) Object;
		proto->set_raw_s("new", new Function(array_new));
		proto->set_raw_s("get", new Function(array_get));
		proto->set_raw_s("set", new Function(array_set));
		proto->set_raw_s("each", new Function(array_each));
		proto->set_raw_s("push", new Function(array_push));
		proto->set_raw_s("pop", new Function(array_pop));
		proto->set_raw_s("unshift", new Function(array_unshift));
		proto->set_raw_s("shift", new Function(array_shift));
		proto->set_property_getter(symbol("length"), new Function(array_length));
		proto->set_raw_s("inspect", new Function(array_inspect));
		proto->set_raw_s("<<", new Function(array_push));
		return proto;
	}
}
<commit_msg>Discovered a whole new type of bug: When a member function of a GC'd object triggers a collection, the value of the "this"-pointer after the collection is no longer valid. So we need to guard against this case.<commit_after>#include "Array.h"
#include "Runtime.h"
#include "SnowString.h"
#include "Function.h"
#include <sstream>

#define DEFAULT_ARRAY_LENGTH 8

namespace snow {
	GC_ROOTS_IMPL(Array) {
		GC_SUPER(Object);

		// If m_AllocatedSize == 0, this is a wrapper around an external array.
		if (m_AllocatedSize)
			GC_ROOT(m_Data);

		for (size_t i = 0; i < m_Length; ++i) {
			GC_ROOT(m_Data[i]);
		}
	}

	void Array::resize(size_t new_size) {
		// TODO: Grow more than strictly necessary to minimize allocations

		// This function contains an allocation, which might trigger a garbage collection,
		// so we need to guard the "this" pointer against this. This would be nice to improve.

		HandleScope _;
		Handle<Array> self = this;

		VALUE* old_pointer = self->m_Data;
		self->m_Data = new(kGarbage, kBlob) VALUE[new_size];
		self->m_AllocatedSize = new_size;
		if (self->m_Length != 0 && old_pointer) {
			memcpy(self->m_Data, old_pointer, self->m_Length*sizeof(VALUE));
		}
	}

	Array::Array(size_t preallocate_length) : Object(array_prototype()), m_Data(NULL), m_Length(0), m_AllocatedSize(0) {
		if (preallocate_length == 0)
			preallocate_length = DEFAULT_ARRAY_LENGTH;
		resize(preallocate_length * sizeof(VALUE));
	}

	Array::Array(const Array& other) : Object(array_prototype()), m_Data(other.m_Data), m_Length(other.m_Length), m_AllocatedSize(0) {
		resize(m_Length * sizeof(VALUE));
	}

	Array::Array(VALUE* existing_array, size_t len, bool copy) : Object(array_prototype()), m_Data(existing_array), m_Length(len), m_AllocatedSize(0) {
		if (copy)
			resize(m_Length * sizeof(VALUE));
	}

	void Array::ensure_length(size_t len) {
		ASSERT(!is_frozen());
		if (len * sizeof(VALUE) <= m_AllocatedSize)
			return;
		resize(len * sizeof(VALUE));
	}

	VALUE& Array::operator[](int64_t idx) {
		if (idx < 0)
			idx %= m_Length;
		if (idx >= (int64_t)m_Length)
		{
			ensure_length(idx+1);
			auto old_length = m_Length;
			m_Length = idx+1;
			for (size_t i = old_length; i < m_Length; ++i) {
				m_Data[i] = nil();
			}
		}
		return m_Data[idx];
	}

	VALUE Array::operator[](int64_t idx) const {
		if (idx < 0)
			idx = m_Length + idx;
		if (idx < 0 || idx >= (int64_t)m_Length)
			return nil();
		return m_Data[idx];
	}

	VALUE Array::push(VALUE val) {
		ensure_length(m_Length + 1);
		m_Data[m_Length] = val;
		++m_Length;
		return val;
	}

	VALUE Array::pop() {
		ASSERT(!is_frozen());
		if (m_Length == 0)
			return nil();
		VALUE val = m_Data[m_Length-1];
		--m_Length;
		return val;
	}

	VALUE Array::unshift(VALUE val) {
		ensure_length(m_Length+1);
		// XXX: For some reason, memmove doesn't work here. Investigate!
		// Also, this might be acceptable, since it's faster than memmove.
		for (size_t i = 0; i < m_Length; ++i) {
			m_Data[m_Length-i] = m_Data[m_Length-1-i];
		}
		++m_Length;
		m_Data[0] = val;
		return val;
	}

	VALUE Array::shift() {
		ASSERT(!is_frozen());
		if (m_Length == 0)
			return nil();
		VALUE val = m_Data[0];
		--m_Length;
		for (size_t i = 0; i < m_Length; ++i) {
			m_Data[i] = m_Data[i+1];
		}
		return val;
	}

	VALUE Array::va_call(VALUE self, uint64_t num_args, va_list& args) {
		if (num_args == 0)
			return this;
		if (num_args == 1) {
			int64_t index = integer(va_arg(args, VALUE));
			return (*this)[index];
		}
		// TODO: ranges and such.
		TRAP();
		return this;
	}

	static VALUE array_new(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		return new Array(args, num_args);
	}

	static VALUE array_get(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		int64_t idx = integer(args[0]);
		return (*array)[idx];
	}

	static VALUE array_set(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 2);
		auto array = object_cast<Array>(self);
		int idx = integer(args[0]);
		VALUE new_value = args[1];
		return array->set_by_index(idx, new_value);
	}

	static VALUE array_each(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args >= 1);
		auto array = object_cast<Array>(self);

		VALUE closure = args[0];
		for (size_t i = 0; i < array->length(); ++i) {
			snow::call(NULL, closure, 2, (*array)[i], value((int64_t)i));
		}
		return self;
	}

	static VALUE array_length(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		auto array = object_cast<Array>(self);
		return value((int64_t)array->length());
	}

	static VALUE array_inspect(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		auto array = object_cast<Array>(self);
		std::stringstream ss;

		ss << "@(";
		for (size_t i = 0; i < array->length(); ++i) {
			ss << value_to_string(snow::call_method((*array)[i], "inspect", 0));
			if (i != array->length() - 1)
				ss << ", ";
		}
		ss << ")";

		return new String(ss.str());
	}

	static VALUE array_push(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		VALUE val = args[0];
		array->push(val);
		return self;
	}

	static VALUE array_pop(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 0);
		auto array = object_cast<Array>(self);
		return array->pop();
	}

	static VALUE array_unshift(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 1);
		auto array = object_cast<Array>(self);
		VALUE val = args[0];
		return array->unshift(val);
	}

	static VALUE array_shift(VALUE self, uint64_t num_args, VALUE* args) {
		NORMAL_SCOPE();
		ASSERT_OBJECT(self, Array);
		ASSERT_ARGS(num_args == 0);
		auto array = object_cast<Array>(self);
		return array->shift();
	}

	Object* array_prototype() {
		static Object* proto = NULL;
		if (proto) return proto;
		proto = new(kMalloc) Object;
		proto->set_raw_s("new", new Function(array_new));
		proto->set_raw_s("get", new Function(array_get));
		proto->set_raw_s("set", new Function(array_set));
		proto->set_raw_s("each", new Function(array_each));
		proto->set_raw_s("push", new Function(array_push));
		proto->set_raw_s("pop", new Function(array_pop));
		proto->set_raw_s("unshift", new Function(array_unshift));
		proto->set_raw_s("shift", new Function(array_shift));
		proto->set_property_getter(symbol("length"), new Function(array_length));
		proto->set_raw_s("inspect", new Function(array_inspect));
		proto->set_raw_s("<<", new Function(array_push));
		return proto;
	}
}
<|endoftext|>