<commit_before>// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Craig Silverstein
//
// This forks out to pprof to do the actual symbolizing.  We might
// be better off writing our own in C++.

#include "config.h"
#include "symbolize.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>   // for write()
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>   // for socketpair() -- needed by Symbolize
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>   // for wait() -- needed by Symbolize
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef __MACH__
#include <mach-o/dyld.h>   // for GetProgramInvocationName()
#include <limits.h>        // for PATH_MAX
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
#include <io.h>            // for get_osfhandle()
#endif
#include <string>
#include "base/commandlineflags.h"
#include "base/logging.h"
#include "base/sysinfo.h"
#if defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif

using std::string;
using tcmalloc::DumpProcSelfMaps;   // from sysinfo.h

DEFINE_string(symbolize_pprof,
              EnvToString("PPROF_PATH", "pprof"),
              "Path to pprof to call for reporting function names.");

// heap_profile_table_pprof may be referenced after destructors are
// called (since that's when leak-checking is done), so we make
// a more-permanent copy that won't ever get destroyed.
static string* g_pprof_path = new string(FLAGS_symbolize_pprof);

// Returns NULL if we're on an OS where we can't get the invocation name.
// Using a static var is ok because we're not called from a thread.
static const char* GetProgramInvocationName() {
#if defined(HAVE_PROGRAM_INVOCATION_NAME)
#ifdef __UCLIBC__
  extern const char* program_invocation_name; // uclibc provides this
#else
  extern char* program_invocation_name;  // gcc provides this
#endif
  return program_invocation_name;
#elif defined(__MACH__)
  // We don't want to allocate memory for this since we may be
  // calculating it when memory is corrupted.
  static char program_invocation_name[PATH_MAX];
  if (program_invocation_name[0] == '\0') {  // first time calculating
    uint32_t length = sizeof(program_invocation_name);
    if (_NSGetExecutablePath(program_invocation_name, &length))
      return NULL;
  }
  return program_invocation_name;
#elif defined(__FreeBSD__)
  static char program_invocation_name[PATH_MAX];
  size_t len = sizeof(program_invocation_name);
  static const int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
  if (!sysctl(name, 4, program_invocation_name, &len, NULL, 0))
    return program_invocation_name;
  return NULL;
#else
  return NULL;   // figure out a way to get argv[0]
#endif
}

// Prints an error message when you can't run Symbolize().
static void PrintError(const char* reason) {
  RAW_LOG(ERROR,
          "*** WARNING: Cannot convert addresses to symbols in output below.\n"
          "*** Reason: %s\n"
          "*** If you cannot fix this, try running pprof directly.\n",
          reason);
}

void SymbolTable::Add(const void* addr) {
  symbolization_table_[addr] = "";
}

const char* SymbolTable::GetSymbol(const void* addr) {
  return symbolization_table_[addr];
}

// Updates symbolization_table with the pointers to symbol names corresponding
// to its keys. The symbol names are stored in out, which is allocated and
// freed by the caller of this routine.
// Note that the forking/etc is not thread-safe or re-entrant.  That's
// ok for the purpose we need -- reporting leaks detected by heap-checker
// -- but be careful if you decide to use this routine for other purposes.
// Returns number of symbols read on error.  If can't symbolize, returns 0
// and emits an error message about why.
int SymbolTable::Symbolize() {
#if !defined(HAVE_UNISTD_H)  || !defined(HAVE_SYS_SOCKET_H) || !defined(HAVE_SYS_WAIT_H)
  PrintError("Perftools does not know how to call a sub-process on this O/S");
  return 0;
#else
  const char* argv0 = GetProgramInvocationName();
  if (argv0 == NULL) {  // can't call symbolize if we can't figure out our name
    PrintError("Cannot figure out the name of this executable (argv0)");
    return 0;
  }
  if (access(g_pprof_path->c_str(), R_OK) != 0) {
    PrintError("Cannot find 'pprof' (is PPROF_PATH set correctly?)");
    return 0;
  }

  // All this work is to do two-way communication.  ugh.
  int *child_in = NULL;   // file descriptors
  int *child_out = NULL;  // for now, we don't worry about child_err
  int child_fds[5][2];    // socketpair may be called up to five times below

  // The client program may close its stdin and/or stdout and/or stderr
  // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
  // In this case the communication between the forked processes may be broken
  // if either the parent or the child tries to close or duplicate these
  // descriptors. The loop below produces two pairs of file descriptors, each
  // greater than 2 (stderr).
  for (int i = 0; i < 5; i++) {
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_fds[i]) == -1) {
      for (int j = 0; j < i; j++) {
        close(child_fds[j][0]);
        close(child_fds[j][1]);
        PrintError("Cannot create a socket pair");
      }
      return 0;
    } else {
      if ((child_fds[i][0] > 2) && (child_fds[i][1] > 2)) {
        if (child_in == NULL) {
          child_in = child_fds[i];
        } else {
          child_out = child_fds[i];
          for (int j = 0; j < i; j++) {
            if (child_fds[j] == child_in) continue;
            close(child_fds[j][0]);
            close(child_fds[j][1]);
          }
          break;
        }
      }
    }
  }

  switch (fork()) {
    case -1: {  // error
      close(child_in[0]);
      close(child_in[1]);
      close(child_out[0]);
      close(child_out[1]);
      PrintError("Unknown error calling fork()");
      return 0;
    }
    case 0: {  // child
      close(child_in[1]);   // child uses the 0's, parent uses the 1's
      close(child_out[1]);  // child uses the 0's, parent uses the 1's
      close(0);
      close(1);
      if (dup2(child_in[0], 0) == -1) _exit(1);
      if (dup2(child_out[0], 1) == -1) _exit(2);
      // Unset vars that might cause trouble when we fork
      unsetenv("CPUPROFILE");
      unsetenv("HEAPPROFILE");
      unsetenv("HEAPCHECK");
      unsetenv("PERFTOOLS_VERBOSE");
      execlp(g_pprof_path->c_str(), g_pprof_path->c_str(),
             "--symbols", argv0, NULL);
      _exit(3);  // if execvp fails, it's bad news for us
    }
    default: {  // parent
      close(child_in[0]);   // child uses the 0's, parent uses the 1's
      close(child_out[0]);  // child uses the 0's, parent uses the 1's
#ifdef HAVE_POLL_H
      // Waiting for 1ms seems to give the OS time to notice any errors.
      poll(0, 0, 1);
      // For maximum safety, we check to make sure the execlp
      // succeeded before trying to write.  (Otherwise we'll get a
      // SIGPIPE.)  For systems without poll.h, we'll just skip this
      // check, and trust that the user set PPROF_PATH correctly!
      struct pollfd pfd = { child_in[1], POLLOUT, 0 };
      if (!poll(&pfd, 1, 0) || !(pfd.revents & POLLOUT) ||
          (pfd.revents & (POLLHUP|POLLERR))) {
        PrintError("Cannot run 'pprof' (is PPROF_PATH set correctly?)");
        return 0;
      }
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
      // On cygwin, DumpProcSelfMaps() takes a HANDLE, not an fd.  Convert.
      const HANDLE symbols_handle = (HANDLE) get_osfhandle(child_in[1]);
      DumpProcSelfMaps(symbols_handle);
#else
      DumpProcSelfMaps(child_in[1]);  // what pprof expects on stdin
#endif

      // Allocate 24 bytes = ("0x" + 8 bytes + "\n" + overhead) for each
      // address to feed to pprof.
      const int kOutBufSize = 24 * symbolization_table_.size();
      char *pprof_buffer = new char[kOutBufSize];
      int written = 0;
      for (SymbolMap::const_iterator iter = symbolization_table_.begin();
           iter != symbolization_table_.end(); ++iter) {
        written += snprintf(pprof_buffer + written, kOutBufSize - written,
                 // pprof expects format to be 0xXXXXXX
                 "0x%" PRIxPTR "\n", reinterpret_cast<uintptr_t>(iter->first));
      }
      write(child_in[1], pprof_buffer, strlen(pprof_buffer));
      close(child_in[1]);             // that's all we need to write

      const int kSymbolBufferSize = kSymbolSize * symbolization_table_.size();
      int total_bytes_read = 0;
      delete[] symbol_buffer_;
      symbol_buffer_ = new char[kSymbolBufferSize];
      memset(symbol_buffer_, '\0', kSymbolBufferSize);
      while (1) {
        int bytes_read = read(child_out[1], symbol_buffer_ + total_bytes_read,
                              kSymbolBufferSize - total_bytes_read);
        if (bytes_read < 0) {
          close(child_out[1]);
          PrintError("Cannot read data from pprof");
          return 0;
        } else if (bytes_read == 0) {
          close(child_out[1]);
          wait(NULL);
          break;
        } else {
          total_bytes_read += bytes_read;
        }
      }
      // We have successfully read the output of pprof into out.  Make sure
      // the last symbol is full (we can tell because it ends with a \n).
      if (total_bytes_read == 0 || symbol_buffer_[total_bytes_read - 1] != '\n')
        return 0;
      // make the symbolization_table_ values point to the output vector
      SymbolMap::iterator fill = symbolization_table_.begin();
      int num_symbols = 0;
      const char *current_name = symbol_buffer_;
      for (int i = 0; i < total_bytes_read; i++) {
        if (symbol_buffer_[i] == '\n') {
          fill->second = current_name;
          symbol_buffer_[i] = '\0';
          current_name = symbol_buffer_ + i + 1;
          fill++;
          num_symbols++;
        }
      }
      return num_symbols;
    }
  }
  PrintError("Unkown error (should never occur!)");
  return 0;  // shouldn't be reachable
#endif
}
<commit_msg>fix memory leak in Symbolize function<commit_after>// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Craig Silverstein
//
// This forks out to pprof to do the actual symbolizing.  We might
// be better off writing our own in C++.

#include "config.h"
#include "symbolize.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>   // for write()
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>   // for socketpair() -- needed by Symbolize
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>   // for wait() -- needed by Symbolize
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef __MACH__
#include <mach-o/dyld.h>   // for GetProgramInvocationName()
#include <limits.h>        // for PATH_MAX
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
#include <io.h>            // for get_osfhandle()
#endif
#include <string>
#include "base/commandlineflags.h"
#include "base/logging.h"
#include "base/sysinfo.h"
#if defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif

using std::string;
using tcmalloc::DumpProcSelfMaps;   // from sysinfo.h

DEFINE_string(symbolize_pprof,
              EnvToString("PPROF_PATH", "pprof"),
              "Path to pprof to call for reporting function names.");

// heap_profile_table_pprof may be referenced after destructors are
// called (since that's when leak-checking is done), so we make
// a more-permanent copy that won't ever get destroyed.
static string* g_pprof_path = new string(FLAGS_symbolize_pprof);

// Returns NULL if we're on an OS where we can't get the invocation name.
// Using a static var is ok because we're not called from a thread.
static const char* GetProgramInvocationName() {
#if defined(HAVE_PROGRAM_INVOCATION_NAME)
#ifdef __UCLIBC__
  extern const char* program_invocation_name; // uclibc provides this
#else
  extern char* program_invocation_name;  // gcc provides this
#endif
  return program_invocation_name;
#elif defined(__MACH__)
  // We don't want to allocate memory for this since we may be
  // calculating it when memory is corrupted.
  static char program_invocation_name[PATH_MAX];
  if (program_invocation_name[0] == '\0') {  // first time calculating
    uint32_t length = sizeof(program_invocation_name);
    if (_NSGetExecutablePath(program_invocation_name, &length))
      return NULL;
  }
  return program_invocation_name;
#elif defined(__FreeBSD__)
  static char program_invocation_name[PATH_MAX];
  size_t len = sizeof(program_invocation_name);
  static const int name[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
  if (!sysctl(name, 4, program_invocation_name, &len, NULL, 0))
    return program_invocation_name;
  return NULL;
#else
  return NULL;   // figure out a way to get argv[0]
#endif
}

// Prints an error message when you can't run Symbolize().
static void PrintError(const char* reason) {
  RAW_LOG(ERROR,
          "*** WARNING: Cannot convert addresses to symbols in output below.\n"
          "*** Reason: %s\n"
          "*** If you cannot fix this, try running pprof directly.\n",
          reason);
}

void SymbolTable::Add(const void* addr) {
  symbolization_table_[addr] = "";
}

const char* SymbolTable::GetSymbol(const void* addr) {
  return symbolization_table_[addr];
}

// Updates symbolization_table with the pointers to symbol names corresponding
// to its keys. The symbol names are stored in out, which is allocated and
// freed by the caller of this routine.
// Note that the forking/etc is not thread-safe or re-entrant.  That's
// ok for the purpose we need -- reporting leaks detected by heap-checker
// -- but be careful if you decide to use this routine for other purposes.
// Returns number of symbols read on error.  If can't symbolize, returns 0
// and emits an error message about why.
int SymbolTable::Symbolize() {
#if !defined(HAVE_UNISTD_H)  || !defined(HAVE_SYS_SOCKET_H) || !defined(HAVE_SYS_WAIT_H)
  PrintError("Perftools does not know how to call a sub-process on this O/S");
  return 0;
#else
  const char* argv0 = GetProgramInvocationName();
  if (argv0 == NULL) {  // can't call symbolize if we can't figure out our name
    PrintError("Cannot figure out the name of this executable (argv0)");
    return 0;
  }
  if (access(g_pprof_path->c_str(), R_OK) != 0) {
    PrintError("Cannot find 'pprof' (is PPROF_PATH set correctly?)");
    return 0;
  }

  // All this work is to do two-way communication.  ugh.
  int *child_in = NULL;   // file descriptors
  int *child_out = NULL;  // for now, we don't worry about child_err
  int child_fds[5][2];    // socketpair may be called up to five times below

  // The client program may close its stdin and/or stdout and/or stderr
  // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
  // In this case the communication between the forked processes may be broken
  // if either the parent or the child tries to close or duplicate these
  // descriptors. The loop below produces two pairs of file descriptors, each
  // greater than 2 (stderr).
  for (int i = 0; i < 5; i++) {
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_fds[i]) == -1) {
      for (int j = 0; j < i; j++) {
        close(child_fds[j][0]);
        close(child_fds[j][1]);
        PrintError("Cannot create a socket pair");
      }
      return 0;
    } else {
      if ((child_fds[i][0] > 2) && (child_fds[i][1] > 2)) {
        if (child_in == NULL) {
          child_in = child_fds[i];
        } else {
          child_out = child_fds[i];
          for (int j = 0; j < i; j++) {
            if (child_fds[j] == child_in) continue;
            close(child_fds[j][0]);
            close(child_fds[j][1]);
          }
          break;
        }
      }
    }
  }

  switch (fork()) {
    case -1: {  // error
      close(child_in[0]);
      close(child_in[1]);
      close(child_out[0]);
      close(child_out[1]);
      PrintError("Unknown error calling fork()");
      return 0;
    }
    case 0: {  // child
      close(child_in[1]);   // child uses the 0's, parent uses the 1's
      close(child_out[1]);  // child uses the 0's, parent uses the 1's
      close(0);
      close(1);
      if (dup2(child_in[0], 0) == -1) _exit(1);
      if (dup2(child_out[0], 1) == -1) _exit(2);
      // Unset vars that might cause trouble when we fork
      unsetenv("CPUPROFILE");
      unsetenv("HEAPPROFILE");
      unsetenv("HEAPCHECK");
      unsetenv("PERFTOOLS_VERBOSE");
      execlp(g_pprof_path->c_str(), g_pprof_path->c_str(),
             "--symbols", argv0, NULL);
      _exit(3);  // if execvp fails, it's bad news for us
    }
    default: {  // parent
      close(child_in[0]);   // child uses the 0's, parent uses the 1's
      close(child_out[0]);  // child uses the 0's, parent uses the 1's
#ifdef HAVE_POLL_H
      // Waiting for 1ms seems to give the OS time to notice any errors.
      poll(0, 0, 1);
      // For maximum safety, we check to make sure the execlp
      // succeeded before trying to write.  (Otherwise we'll get a
      // SIGPIPE.)  For systems without poll.h, we'll just skip this
      // check, and trust that the user set PPROF_PATH correctly!
      struct pollfd pfd = { child_in[1], POLLOUT, 0 };
      if (!poll(&pfd, 1, 0) || !(pfd.revents & POLLOUT) ||
          (pfd.revents & (POLLHUP|POLLERR))) {
        PrintError("Cannot run 'pprof' (is PPROF_PATH set correctly?)");
        return 0;
      }
#endif
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
      // On cygwin, DumpProcSelfMaps() takes a HANDLE, not an fd.  Convert.
      const HANDLE symbols_handle = (HANDLE) get_osfhandle(child_in[1]);
      DumpProcSelfMaps(symbols_handle);
#else
      DumpProcSelfMaps(child_in[1]);  // what pprof expects on stdin
#endif

      // Allocate 24 bytes = ("0x" + 8 bytes + "\n" + overhead) for each
      // address to feed to pprof.
      const int kOutBufSize = 24 * symbolization_table_.size();
      char *pprof_buffer = new char[kOutBufSize];
      int written = 0;
      for (SymbolMap::const_iterator iter = symbolization_table_.begin();
           iter != symbolization_table_.end(); ++iter) {
        written += snprintf(pprof_buffer + written, kOutBufSize - written,
                 // pprof expects format to be 0xXXXXXX
                 "0x%" PRIxPTR "\n", reinterpret_cast<uintptr_t>(iter->first));
      }
      write(child_in[1], pprof_buffer, strlen(pprof_buffer));
      close(child_in[1]);             // that's all we need to write
      delete[] pprof_buffer;

      const int kSymbolBufferSize = kSymbolSize * symbolization_table_.size();
      int total_bytes_read = 0;
      delete[] symbol_buffer_;
      symbol_buffer_ = new char[kSymbolBufferSize];
      memset(symbol_buffer_, '\0', kSymbolBufferSize);
      while (1) {
        int bytes_read = read(child_out[1], symbol_buffer_ + total_bytes_read,
                              kSymbolBufferSize - total_bytes_read);
        if (bytes_read < 0) {
          close(child_out[1]);
          PrintError("Cannot read data from pprof");
          return 0;
        } else if (bytes_read == 0) {
          close(child_out[1]);
          wait(NULL);
          break;
        } else {
          total_bytes_read += bytes_read;
        }
      }
      // We have successfully read the output of pprof into out.  Make sure
      // the last symbol is full (we can tell because it ends with a \n).
      if (total_bytes_read == 0 || symbol_buffer_[total_bytes_read - 1] != '\n')
        return 0;
      // make the symbolization_table_ values point to the output vector
      SymbolMap::iterator fill = symbolization_table_.begin();
      int num_symbols = 0;
      const char *current_name = symbol_buffer_;
      for (int i = 0; i < total_bytes_read; i++) {
        if (symbol_buffer_[i] == '\n') {
          fill->second = current_name;
          symbol_buffer_[i] = '\0';
          current_name = symbol_buffer_ + i + 1;
          fill++;
          num_symbols++;
        }
      }
      return num_symbols;
    }
  }
  PrintError("Unkown error (should never occur!)");
  return 0;  // shouldn't be reachable
#endif
}
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (C) 2006, IBM                                               *
 *                                                                         *
 *   Maintained By:                                                        *
 *   Eric Munson and Brad Peters                                           *
 *   munsone@us.ibm.com, bpeters@us.ibm.com                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the Lesser GNU General Public License as        *
 *   published by the Free Software Foundation; either version 2.1 of the  *
 *   License, or at your option) 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 Lesser General Public License for more details.                   *
 *                                                                         *
 *   You should have received a copy of the Lesser GNU General Public      *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <libvpd-2/vpddbenv.hpp>
#include <libvpd-2/logger.hpp>
#include <libvpd-2/debug.hpp>

#include <sstream>
#include <cstring>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

using namespace::std;

namespace lsvpd
{
	const string VpdDbEnv::TABLE_NAME ( "components" );
	const string VpdDbEnv::ID         ( "comp_id" );
	const string VpdDbEnv::DATA       ( "comp_data" );

	/*
	 * The copy constructor is marked private but these things need to be
	 * filled out to compile correctly.
	 */
	 VpdDbEnv::VpdDbEnv( const VpdDbEnv& copyMe ):
		mDbFileName( copyMe.mDbFileName ), mEnvDir( copyMe.mEnvDir ),
		mpVpdDb( NULL )
		{ }

	VpdDbEnv::VpdDbEnv( const string& envDir, const string& dbFileName,
				bool readOnly = false ) :
				mDbFileName( dbFileName ),
				mEnvDir( envDir ),
				mpVpdDb( NULL )
	{
		int rc;
		Logger l;
		ostringstream message;

		struct stat st;
		bool dbExists;

		mDbPath = mEnvDir + "/" + mDbFileName;
		dbExists = (stat( mDbPath.c_str( ), &st )) == 0;
		if (readOnly) {
			if (!dbExists) {
				message << "DB requested for reading does not "
					"exist (" << errno << ":" <<
					strerror(errno) << ")." << endl;
				goto CON_ERR;
			}
		}

		rc = sqlite3_open( mDbPath.c_str( ), &mpVpdDb );
		if( rc != SQLITE_OK )
		{
			message << "SQLITE Error " << rc << ": " <<
				sqlite3_errmsg( mpVpdDb ) << endl;
			goto CON_ERR;
		}

		if( !dbExists )
		{
			sqlite3_stmt *pstmt;
			const char *out;
			ostringstream sql;
			sql << "CREATE TABLE " << TABLE_NAME << " ( " << ID <<
				" TEXT NOT NULL UNIQUE, " << DATA << " BLOB NOT NULL );";
			string stmt = sql.str( );
			rc = SQLITE3_PREPARE( mpVpdDb, stmt.c_str( ),
						stmt.length( ) + 1, &pstmt, &out );
			if( rc != SQLITE_OK )
			{
				message << "SQLITE Error " << rc << ": " <<
					sqlite3_errmsg( mpVpdDb ) << endl;
				goto CON_ERR;
			}

			rc = sqlite3_step( pstmt );
			if( rc != SQLITE_DONE )
			{
				message << "SQLITE Error " << rc << ": " <<
					sqlite3_errmsg( mpVpdDb ) << endl;
				goto CON_ERR;
			}
			sqlite3_finalize( pstmt );
		}
		return;

CON_ERR:
		l.log( message.str( ), LOG_ERR );
		VpdException ve( message.str( ) );
		if( mpVpdDb != NULL )
			sqlite3_close( mpVpdDb );
		throw ve;
	}

	VpdDbEnv::~VpdDbEnv()
	{
		int rc;
		rc = sqlite3_close( mpVpdDb );
		if( rc != SQLITE_OK )
		{
			Logger l;
			ostringstream message;
			message << "SQLITE Error " << rc << ": " <<
				sqlite3_errmsg( mpVpdDb ) << endl;
			l.log( message.str( ), LOG_ERR );
			VpdException ve( message.str( ) );
			throw ve;
		}
	}

	Component* VpdDbEnv::fetch( const string& deviceID )
	{
		Component* ret = NULL;
		sqlite3_stmt *pstmt = NULL;
		int rc;
		const char *out;
		ostringstream message;
		string sql = "SELECT " + DATA + " FROM " + TABLE_NAME + " WHERE " +
				ID + "=?";

		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto FETCH_COMP_ERR;

		rc = sqlite3_bind_text(pstmt, 1, deviceID.c_str(),
				       deviceID.length(), SQLITE_STATIC);
		if (rc != SQLITE_OK)
			goto FETCH_COMP_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_ROW && rc != SQLITE_DONE )
			goto FETCH_COMP_ERR;

		if( rc == SQLITE_ROW ) {
			try {
				ret = new Component( sqlite3_column_blob( pstmt, 0 ) );
			}
			catch (std::bad_alloc& ba) {
				message << "SQLITE Error: call to new() failed " << endl;
				goto FETCH_NEW_FAILED;
			}
		}

		sqlite3_finalize( pstmt );
		return ret;

FETCH_COMP_ERR:
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
FETCH_NEW_FAILED:
		Logger().log( message.str( ), LOG_ERR );
		if( pstmt )
			sqlite3_finalize( pstmt );
		return ret;
	}

	/*
	 * Fetches the system root (the base of the component tree that
	 * stores all the system-wide vpd.
	 */
	System* VpdDbEnv::fetch( )
	{
		System* ret = NULL;
		sqlite3_stmt *pstmt = NULL;
		int rc;
		const char *out;
		ostringstream message;

		string sql = "SELECT " + DATA + " FROM " + TABLE_NAME + " WHERE " +
				ID + "='" + System::ID + "';";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto FETCH_SYS_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_ROW && rc != SQLITE_DONE )
			goto FETCH_SYS_ERR;

		if( rc == SQLITE_ROW ) {
			try {
				ret = new System( sqlite3_column_blob( pstmt, 0 ) );
			}
			catch (std::bad_alloc& ba) {
				message << "SQLITE Error: call to new() failed " << endl;
				goto FETCH_NEW_FAILED;
			}
		}

		sqlite3_finalize( pstmt );
		return ret;

FETCH_SYS_ERR:
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
FETCH_NEW_FAILED:
		Logger().log( message.str( ), LOG_ERR );
		if( pstmt )
			sqlite3_finalize( pstmt );
		return ret;
	}

	bool VpdDbEnv::store( Component *storeMe )
	{
		void * buffer = NULL;
		unsigned int dataSize;
		int rc;
		const char *out;
		sqlite3_stmt *pstmt = NULL;

		string sql = "INSERT INTO " + TABLE_NAME + " (" + ID + ", " + DATA +
				") VALUES ('" + storeMe->getID( ) + "', ?);";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto STORE_COMP_ERR;

		dataSize = storeMe->pack( &buffer );
		rc = sqlite3_bind_blob( pstmt, 1, buffer, dataSize, SQLITE_TRANSIENT );
		if( rc != SQLITE_OK )
			goto STORE_COMP_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_DONE )
			goto STORE_COMP_ERR;

		if( buffer != NULL )
			delete [] (char*)buffer;
		sqlite3_finalize( pstmt );
		return true;

STORE_COMP_ERR:
		if( buffer != NULL )
			delete [] (char*)buffer;
		Logger l;
		ostringstream message;
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
		l.log( message.str( ), LOG_ERR );
		sqlite3_finalize( pstmt );
		return false;
	}

	bool VpdDbEnv::store( System *storeMe )
	{
		void * buffer = NULL;
		unsigned int dataSize;
		int rc;
		const char *out;
		sqlite3_stmt *pstmt = NULL;

		string sql = "INSERT INTO " + TABLE_NAME + " (" + ID + ", " + DATA +
				") VALUES ('" + storeMe->getID( ) + "', ?);";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto STORE_SYS_ERR;

		dataSize = storeMe->pack( &buffer );
		rc = sqlite3_bind_blob( pstmt, 1, buffer, dataSize, SQLITE_TRANSIENT );
		if( rc != SQLITE_OK )
			goto STORE_SYS_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_DONE )
			goto STORE_SYS_ERR;

		if( buffer != NULL )
			delete [] (char*)buffer;
		sqlite3_finalize( pstmt );
		return true;

STORE_SYS_ERR:
		if( buffer != NULL )
			delete [] (char*)buffer;
		Logger l;
		ostringstream message;
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
		l.log( message.str( ), LOG_ERR );
		sqlite3_finalize( pstmt );
		return false;
	}

	bool VpdDbEnv::remove( const string& deviceID )
	{
		int rc;
		const char *out;
		sqlite3_stmt *pstmt = NULL;

		string sql = "DELETE FROM " + TABLE_NAME + " WHERE " + ID + "='" +
			deviceID + "';";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto REMOVE_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_DONE )
			goto REMOVE_ERR;
		sqlite3_finalize( pstmt );
		return true;

REMOVE_ERR:
		Logger l;
		ostringstream message;
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
		l.log( message.str( ), LOG_ERR );
		sqlite3_finalize( pstmt );
		return false;
	}

	vector<string> VpdDbEnv::getKeys( )
	{
		vector<string> ret;
		sqlite3_stmt *pstmt = NULL;
		int rc;
		const char *out;

		string sql = "SELECT " + ID + " FROM " + TABLE_NAME + ";";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
		{
			Logger l;
			ostringstream message;
			message << "SQLITE Error " << rc << ": " <<
				sqlite3_errmsg( mpVpdDb ) << endl;
			l.log( message.str( ), LOG_ERR );
			return ret;
		}

		rc = sqlite3_step( pstmt );
		while( rc == SQLITE_ROW )
		{
			const char *row = (const char*)sqlite3_column_text( pstmt, 0 );
			if( row )
				ret.push_back( string( row ) );
			rc = sqlite3_step( pstmt );
		}
		sqlite3_finalize( pstmt );
		return ret;
	}
}
<commit_msg>libvpd: Do not throw exception in destructor<commit_after>/***************************************************************************
 *   Copyright (C) 2006, IBM                                               *
 *                                                                         *
 *   Maintained By:                                                        *
 *   Eric Munson and Brad Peters                                           *
 *   munsone@us.ibm.com, bpeters@us.ibm.com                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the Lesser GNU General Public License as        *
 *   published by the Free Software Foundation; either version 2.1 of the  *
 *   License, or at your option) 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 Lesser General Public License for more details.                   *
 *                                                                         *
 *   You should have received a copy of the Lesser GNU General Public      *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <libvpd-2/vpddbenv.hpp>
#include <libvpd-2/logger.hpp>
#include <libvpd-2/debug.hpp>

#include <sstream>
#include <cstring>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

using namespace::std;

namespace lsvpd
{
	const string VpdDbEnv::TABLE_NAME ( "components" );
	const string VpdDbEnv::ID         ( "comp_id" );
	const string VpdDbEnv::DATA       ( "comp_data" );

	/*
	 * The copy constructor is marked private but these things need to be
	 * filled out to compile correctly.
	 */
	 VpdDbEnv::VpdDbEnv( const VpdDbEnv& copyMe ):
		mDbFileName( copyMe.mDbFileName ), mEnvDir( copyMe.mEnvDir ),
		mpVpdDb( NULL )
		{ }

	VpdDbEnv::VpdDbEnv( const string& envDir, const string& dbFileName,
				bool readOnly = false ) :
				mDbFileName( dbFileName ),
				mEnvDir( envDir ),
				mpVpdDb( NULL )
	{
		int rc;
		Logger l;
		ostringstream message;

		struct stat st;
		bool dbExists;

		mDbPath = mEnvDir + "/" + mDbFileName;
		dbExists = (stat( mDbPath.c_str( ), &st )) == 0;
		if (readOnly) {
			if (!dbExists) {
				message << "DB requested for reading does not "
					"exist (" << errno << ":" <<
					strerror(errno) << ")." << endl;
				goto CON_ERR;
			}
		}

		rc = sqlite3_open( mDbPath.c_str( ), &mpVpdDb );
		if( rc != SQLITE_OK )
		{
			message << "SQLITE Error " << rc << ": " <<
				sqlite3_errmsg( mpVpdDb ) << endl;
			goto CON_ERR;
		}

		if( !dbExists )
		{
			sqlite3_stmt *pstmt;
			const char *out;
			ostringstream sql;
			sql << "CREATE TABLE " << TABLE_NAME << " ( " << ID <<
				" TEXT NOT NULL UNIQUE, " << DATA << " BLOB NOT NULL );";
			string stmt = sql.str( );
			rc = SQLITE3_PREPARE( mpVpdDb, stmt.c_str( ),
						stmt.length( ) + 1, &pstmt, &out );
			if( rc != SQLITE_OK )
			{
				message << "SQLITE Error " << rc << ": " <<
					sqlite3_errmsg( mpVpdDb ) << endl;
				goto CON_ERR;
			}

			rc = sqlite3_step( pstmt );
			if( rc != SQLITE_DONE )
			{
				message << "SQLITE Error " << rc << ": " <<
					sqlite3_errmsg( mpVpdDb ) << endl;
				goto CON_ERR;
			}
			sqlite3_finalize( pstmt );
		}
		return;

CON_ERR:
		l.log( message.str( ), LOG_ERR );
		VpdException ve( message.str( ) );
		if( mpVpdDb != NULL )
			sqlite3_close( mpVpdDb );
		throw ve;
	}

	VpdDbEnv::~VpdDbEnv()
	{
		int rc;
		rc = sqlite3_close( mpVpdDb );
		if( rc != SQLITE_OK )
		{
			Logger l;
			ostringstream message;
			message << "SQLITE Error " << rc << ": " <<
				sqlite3_errmsg( mpVpdDb ) << endl;
			l.log( message.str( ), LOG_ERR );
		}
	}

	Component* VpdDbEnv::fetch( const string& deviceID )
	{
		Component* ret = NULL;
		sqlite3_stmt *pstmt = NULL;
		int rc;
		const char *out;
		ostringstream message;
		string sql = "SELECT " + DATA + " FROM " + TABLE_NAME + " WHERE " +
				ID + "=?";

		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto FETCH_COMP_ERR;

		rc = sqlite3_bind_text(pstmt, 1, deviceID.c_str(),
				       deviceID.length(), SQLITE_STATIC);
		if (rc != SQLITE_OK)
			goto FETCH_COMP_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_ROW && rc != SQLITE_DONE )
			goto FETCH_COMP_ERR;

		if( rc == SQLITE_ROW ) {
			try {
				ret = new Component( sqlite3_column_blob( pstmt, 0 ) );
			}
			catch (std::bad_alloc& ba) {
				message << "SQLITE Error: call to new() failed " << endl;
				goto FETCH_NEW_FAILED;
			}
		}

		sqlite3_finalize( pstmt );
		return ret;

FETCH_COMP_ERR:
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
FETCH_NEW_FAILED:
		Logger().log( message.str( ), LOG_ERR );
		if( pstmt )
			sqlite3_finalize( pstmt );
		return ret;
	}

	/*
	 * Fetches the system root (the base of the component tree that
	 * stores all the system-wide vpd.
	 */
	System* VpdDbEnv::fetch( )
	{
		System* ret = NULL;
		sqlite3_stmt *pstmt = NULL;
		int rc;
		const char *out;
		ostringstream message;

		string sql = "SELECT " + DATA + " FROM " + TABLE_NAME + " WHERE " +
				ID + "='" + System::ID + "';";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto FETCH_SYS_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_ROW && rc != SQLITE_DONE )
			goto FETCH_SYS_ERR;

		if( rc == SQLITE_ROW ) {
			try {
				ret = new System( sqlite3_column_blob( pstmt, 0 ) );
			}
			catch (std::bad_alloc& ba) {
				message << "SQLITE Error: call to new() failed " << endl;
				goto FETCH_NEW_FAILED;
			}
		}

		sqlite3_finalize( pstmt );
		return ret;

FETCH_SYS_ERR:
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
FETCH_NEW_FAILED:
		Logger().log( message.str( ), LOG_ERR );
		if( pstmt )
			sqlite3_finalize( pstmt );
		return ret;
	}

	bool VpdDbEnv::store( Component *storeMe )
	{
		void * buffer = NULL;
		unsigned int dataSize;
		int rc;
		const char *out;
		sqlite3_stmt *pstmt = NULL;

		string sql = "INSERT INTO " + TABLE_NAME + " (" + ID + ", " + DATA +
				") VALUES ('" + storeMe->getID( ) + "', ?);";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto STORE_COMP_ERR;

		dataSize = storeMe->pack( &buffer );
		rc = sqlite3_bind_blob( pstmt, 1, buffer, dataSize, SQLITE_TRANSIENT );
		if( rc != SQLITE_OK )
			goto STORE_COMP_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_DONE )
			goto STORE_COMP_ERR;

		if( buffer != NULL )
			delete [] (char*)buffer;
		sqlite3_finalize( pstmt );
		return true;

STORE_COMP_ERR:
		if( buffer != NULL )
			delete [] (char*)buffer;
		Logger l;
		ostringstream message;
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
		l.log( message.str( ), LOG_ERR );
		sqlite3_finalize( pstmt );
		return false;
	}

	bool VpdDbEnv::store( System *storeMe )
	{
		void * buffer = NULL;
		unsigned int dataSize;
		int rc;
		const char *out;
		sqlite3_stmt *pstmt = NULL;

		string sql = "INSERT INTO " + TABLE_NAME + " (" + ID + ", " + DATA +
				") VALUES ('" + storeMe->getID( ) + "', ?);";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto STORE_SYS_ERR;

		dataSize = storeMe->pack( &buffer );
		rc = sqlite3_bind_blob( pstmt, 1, buffer, dataSize, SQLITE_TRANSIENT );
		if( rc != SQLITE_OK )
			goto STORE_SYS_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_DONE )
			goto STORE_SYS_ERR;

		if( buffer != NULL )
			delete [] (char*)buffer;
		sqlite3_finalize( pstmt );
		return true;

STORE_SYS_ERR:
		if( buffer != NULL )
			delete [] (char*)buffer;
		Logger l;
		ostringstream message;
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
		l.log( message.str( ), LOG_ERR );
		sqlite3_finalize( pstmt );
		return false;
	}

	bool VpdDbEnv::remove( const string& deviceID )
	{
		int rc;
		const char *out;
		sqlite3_stmt *pstmt = NULL;

		string sql = "DELETE FROM " + TABLE_NAME + " WHERE " + ID + "='" +
			deviceID + "';";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
			goto REMOVE_ERR;

		rc = sqlite3_step( pstmt );
		if( rc != SQLITE_DONE )
			goto REMOVE_ERR;
		sqlite3_finalize( pstmt );
		return true;

REMOVE_ERR:
		Logger l;
		ostringstream message;
		message << "SQLITE Error " << rc << ": " <<
			sqlite3_errmsg( mpVpdDb ) << endl;
		l.log( message.str( ), LOG_ERR );
		sqlite3_finalize( pstmt );
		return false;
	}

	vector<string> VpdDbEnv::getKeys( )
	{
		vector<string> ret;
		sqlite3_stmt *pstmt = NULL;
		int rc;
		const char *out;

		string sql = "SELECT " + ID + " FROM " + TABLE_NAME + ";";
		rc = SQLITE3_PREPARE( mpVpdDb, sql.c_str( ), sql.length( ) + 1,
					&pstmt, &out );
		if( rc != SQLITE_OK )
		{
			Logger l;
			ostringstream message;
			message << "SQLITE Error " << rc << ": " <<
				sqlite3_errmsg( mpVpdDb ) << endl;
			l.log( message.str( ), LOG_ERR );
			return ret;
		}

		rc = sqlite3_step( pstmt );
		while( rc == SQLITE_ROW )
		{
			const char *row = (const char*)sqlite3_column_text( pstmt, 0 );
			if( row )
				ret.push_back( string( row ) );
			rc = sqlite3_step( pstmt );
		}
		sqlite3_finalize( pstmt );
		return ret;
	}
}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Raine Makelainen <raine.makelainen@jolla.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "webpages.h"
#include "declarativewebcontainer.h"
#include "declarativewebpage.h"
#include "qmozcontext.h"

#include <QDateTime>
#include <QDBusConnection>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QQmlContext>
#include <QMapIterator>
#include <QRectF>
#include <qqmlinfo.h>

#ifndef DEBUG_LOGS
#define DEBUG_LOGS 0
#endif

#if DEBUG_LOGS
#include <QDebug>
#endif

static const qint64 gMemoryPressureTimeout = 600 * 1000; // 600 sec

WebPages::WebPages(QObject *parent)
    : QObject(parent)
    , m_backgroundTimestamp(0)
{
    QDBusConnection::systemBus().connect("com.nokia.mce", "/com/nokia/mce/signal",
                                         "com.nokia.mce.signal", "sig_memory_level_ind",
                                         this, SLOT(handleMemNotify(QString)));
}

WebPages::~WebPages()
{
}

void WebPages::initialize(DeclarativeWebContainer *webContainer, QQmlComponent *webPageComponent)
{
    if (!m_webContainer || !m_webPageComponent) {
        m_webContainer = webContainer;
        m_webPageComponent = webPageComponent;
    }

    connect(webContainer, SIGNAL(foregroundChanged()), this, SLOT(updateBackgroundTimestamp()));
}

void WebPages::updateBackgroundTimestamp()
{
    if (!m_webContainer->foreground()) {
        m_backgroundTimestamp = QDateTime::currentMSecsSinceEpoch();
    }
}

bool WebPages::initialized() const
{
    return m_webContainer && m_webPageComponent;
}

int WebPages::count() const
{
    return m_activePages.count();
}

bool WebPages::setMaxLivePages(int count)
{
    return m_activePages.setMaxLivePages(count);
}

int WebPages::maxLivePages() const
{
    return m_activePages.maxLivePages();
}

bool WebPages::alive(int tabId) const
{
    return m_activePages.alive(tabId);
}

WebPageActivationData WebPages::page(int tabId, int parentId)
{
    if (!m_webPageComponent) {
        qWarning() << "TabModel not initialized!";
        return WebPageActivationData(0, false);
    }

    if (m_activePages.active(tabId)) {
        DeclarativeWebPage *activePage = m_activePages.activeWebPage();
        activePage->resumeView();
        activePage->setVisible(true);
        return WebPageActivationData(activePage, false);
    }

#if DEBUG_LOGS
    qDebug() << "about to create a new tab or activate old:" << tabId;
#endif

    DeclarativeWebPage *webPage = 0;
    DeclarativeWebPage *oldActiveWebPage = m_activePages.activeWebPage();
    if (!m_activePages.alive(tabId)) {
        QQmlContext *creationContext = m_webPageComponent->creationContext();
        QQmlContext *context = new QQmlContext(creationContext ? creationContext : QQmlEngine::contextForObject(m_webContainer));
        QObject *object = m_webPageComponent->beginCreate(context);
        if (object) {
            context->setParent(object);
            object->setParent(m_webContainer);
            webPage = qobject_cast<DeclarativeWebPage *>(object);
            if (webPage) {
                webPage->setParentItem(m_webContainer);
                webPage->setParentID(parentId);
                webPage->setTabId(tabId);
                webPage->setContainer(m_webContainer);
                m_webPageComponent->completeCreate();
#if DEBUG_LOGS
                qDebug() << "New view id:" << webPage->uniqueID() << "parentId:" << webPage->parentId() << "tab id:" << webPage->tabId();
#endif
                m_activePages.prepend(tabId, webPage);
                QQmlEngine::setObjectOwnership(webPage, QQmlEngine::CppOwnership);
            } else {
                qmlInfo(m_webContainer) << "webPage component must be a WebPage component";
            }
        } else {
            qmlInfo(m_webContainer) << "Creation of the web page failed. Error: " << m_webPageComponent->errorString();
            delete object;
            object = 0;
        }
    }

    DeclarativeWebPage *newActiveWebPage = m_activePages.activate(tabId);
    updateStates(oldActiveWebPage, newActiveWebPage);

#if DEBUG_LOGS
    dumpPages();
#endif

    return WebPageActivationData(newActiveWebPage, true);
}

void WebPages::release(int tabId, bool virtualize)
{
    m_activePages.release(tabId, virtualize);
}

void WebPages::clear()
{
    m_activePages.clear();
}

int WebPages::parentTabId(int tabId) const
{
    return m_activePages.parentTabId(tabId);
}

void WebPages::updateStates(DeclarativeWebPage *oldActivePage, DeclarativeWebPage *newActivePage)
{
    if (oldActivePage) {
        oldActivePage->setVisible(false);
        oldActivePage->setOpacity(1.0);

        // Allow suspending only the current active page if it is not the creator (parent).
        if (newActivePage->parentId() != (int)oldActivePage->uniqueID()) {
            if (oldActivePage->loading()) {
                oldActivePage->stop();
            }
            oldActivePage->suspendView();
        }
    }

    if (newActivePage) {
        newActivePage->resumeView();
        newActivePage->setVisible(true);
    }
}

void WebPages::dumpPages() const
{
    m_activePages.dumpPages();
}

void WebPages::handleMemNotify(const QString &memoryLevel)
{
    if (memoryLevel == QString("warning") || memoryLevel == QString("critical")) {
        m_activePages.virtualizeInactive();

        if (!m_webContainer->foreground() &&
                (QDateTime::currentMSecsSinceEpoch() - m_backgroundTimestamp) > gMemoryPressureTimeout) {
            m_backgroundTimestamp = QDateTime::currentMSecsSinceEpoch();
            QMozContext::GetInstance()->sendObserve(QString("memory-pressure"), QString("heap-minimize"));
        }
    }
}
<commit_msg>[sailfish-browser] Make low memory handling optional<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Raine Makelainen <raine.makelainen@jolla.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "webpages.h"
#include "declarativewebcontainer.h"
#include "declarativewebpage.h"
#include "qmozcontext.h"

#include <QDateTime>
#include <QDBusConnection>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QQmlContext>
#include <QMapIterator>
#include <QRectF>
#include <qqmlinfo.h>

#ifndef DEBUG_LOGS
#define DEBUG_LOGS 0
#endif

#if DEBUG_LOGS
#include <QDebug>
#endif

static const qint64 gMemoryPressureTimeout = 600 * 1000; // 600 sec
// In normal cases gLowMemoryEnabled is true. Can be disabled e.g. for test runs.
static const bool gLowMemoryEnabled = qgetenv("LOW_MEMORY_DISABLED").isEmpty();

WebPages::WebPages(QObject *parent)
    : QObject(parent)
    , m_backgroundTimestamp(0)
{
    if (gLowMemoryEnabled) {
        QDBusConnection::systemBus().connect("com.nokia.mce", "/com/nokia/mce/signal",
                                             "com.nokia.mce.signal", "sig_memory_level_ind",
                                             this, SLOT(handleMemNotify(QString)));
    }
}

WebPages::~WebPages()
{
}

void WebPages::initialize(DeclarativeWebContainer *webContainer, QQmlComponent *webPageComponent)
{
    if (!m_webContainer || !m_webPageComponent) {
        m_webContainer = webContainer;
        m_webPageComponent = webPageComponent;
    }

    connect(webContainer, SIGNAL(foregroundChanged()), this, SLOT(updateBackgroundTimestamp()));
}

void WebPages::updateBackgroundTimestamp()
{
    if (!m_webContainer->foreground()) {
        m_backgroundTimestamp = QDateTime::currentMSecsSinceEpoch();
    }
}

bool WebPages::initialized() const
{
    return m_webContainer && m_webPageComponent;
}

int WebPages::count() const
{
    return m_activePages.count();
}

bool WebPages::setMaxLivePages(int count)
{
    return m_activePages.setMaxLivePages(count);
}

int WebPages::maxLivePages() const
{
    return m_activePages.maxLivePages();
}

bool WebPages::alive(int tabId) const
{
    return m_activePages.alive(tabId);
}

WebPageActivationData WebPages::page(int tabId, int parentId)
{
    if (!m_webPageComponent) {
        qWarning() << "TabModel not initialized!";
        return WebPageActivationData(0, false);
    }

    if (m_activePages.active(tabId)) {
        DeclarativeWebPage *activePage = m_activePages.activeWebPage();
        activePage->resumeView();
        activePage->setVisible(true);
        return WebPageActivationData(activePage, false);
    }

#if DEBUG_LOGS
    qDebug() << "about to create a new tab or activate old:" << tabId;
#endif

    DeclarativeWebPage *webPage = 0;
    DeclarativeWebPage *oldActiveWebPage = m_activePages.activeWebPage();
    if (!m_activePages.alive(tabId)) {
        QQmlContext *creationContext = m_webPageComponent->creationContext();
        QQmlContext *context = new QQmlContext(creationContext ? creationContext : QQmlEngine::contextForObject(m_webContainer));
        QObject *object = m_webPageComponent->beginCreate(context);
        if (object) {
            context->setParent(object);
            object->setParent(m_webContainer);
            webPage = qobject_cast<DeclarativeWebPage *>(object);
            if (webPage) {
                webPage->setParentItem(m_webContainer);
                webPage->setParentID(parentId);
                webPage->setTabId(tabId);
                webPage->setContainer(m_webContainer);
                m_webPageComponent->completeCreate();
#if DEBUG_LOGS
                qDebug() << "New view id:" << webPage->uniqueID() << "parentId:" << webPage->parentId() << "tab id:" << webPage->tabId();
#endif
                m_activePages.prepend(tabId, webPage);
                QQmlEngine::setObjectOwnership(webPage, QQmlEngine::CppOwnership);
            } else {
                qmlInfo(m_webContainer) << "webPage component must be a WebPage component";
            }
        } else {
            qmlInfo(m_webContainer) << "Creation of the web page failed. Error: " << m_webPageComponent->errorString();
            delete object;
            object = 0;
        }
    }

    DeclarativeWebPage *newActiveWebPage = m_activePages.activate(tabId);
    updateStates(oldActiveWebPage, newActiveWebPage);

#if DEBUG_LOGS
    dumpPages();
#endif

    return WebPageActivationData(newActiveWebPage, true);
}

void WebPages::release(int tabId, bool virtualize)
{
    m_activePages.release(tabId, virtualize);
}

void WebPages::clear()
{
    m_activePages.clear();
}

int WebPages::parentTabId(int tabId) const
{
    return m_activePages.parentTabId(tabId);
}

void WebPages::updateStates(DeclarativeWebPage *oldActivePage, DeclarativeWebPage *newActivePage)
{
    if (oldActivePage) {
        oldActivePage->setVisible(false);
        oldActivePage->setOpacity(1.0);

        // Allow suspending only the current active page if it is not the creator (parent).
        if (newActivePage->parentId() != (int)oldActivePage->uniqueID()) {
            if (oldActivePage->loading()) {
                oldActivePage->stop();
            }
            oldActivePage->suspendView();
        }
    }

    if (newActivePage) {
        newActivePage->resumeView();
        newActivePage->setVisible(true);
    }
}

void WebPages::dumpPages() const
{
    m_activePages.dumpPages();
}

void WebPages::handleMemNotify(const QString &memoryLevel)
{
    if (memoryLevel == QString("warning") || memoryLevel == QString("critical")) {
        m_activePages.virtualizeInactive();

        if (!m_webContainer->foreground() &&
                (QDateTime::currentMSecsSinceEpoch() - m_backgroundTimestamp) > gMemoryPressureTimeout) {
            m_backgroundTimestamp = QDateTime::currentMSecsSinceEpoch();
            QMozContext::GetInstance()->sendObserve(QString("memory-pressure"), QString("heap-minimize"));
        }
    }
}
<|endoftext|>


<commit_before>#include <iostream>
#include <thread>
#include <sstream>

#include <console/console.h>

class TestConsole : public console::Console {
public:
	TestConsole() {
	}

protected:
	void printColor(console::Color color) {
		switch (color) {
			case console::Color::BLACK:
				print("BLACK");
				break;
			case console::Color::DARKBLUE:
				print("DARKBLUE");
				break;
			case console::Color::DARKGREEN:
				print("DARKGREEN");
				break;
			case console::Color::DARKCYAN:
				print("DARKCYAN");
				break;
			case console::Color::DARKRED:
				print("DARKRED");
				break;
			case console::Color::DARKMAGENTA:
				print("DARKMAGENTA");
				break;
			case console::Color::DARKYELLOW:
				print("DARKYELLOW");
				break;
			case console::Color::DARKGREY:
				print("DARKGREY");
				break;
			case console::Color::GREY:
				print("GREY");
				break;
			case console::Color::BLUE:
				print("BLUE");
				break;
			case console::Color::GREEN:
				print("GREEN");
				break;
			case console::Color::CYAN:
				print("CYAN");
				break;
			case console::Color::RED:
				print("RED");
				break;
			case console::Color::MAGENTA:
				print("MAGENTA");
				break;
			case console::Color::YELLOW:
				print("YELLOW");
				break;
			case console::Color::WHITE:
				print("WHITE");
				break;
		}
	}

	void initPreLoop() override {
		setTitle("Test");

		setCursorPosition(10, 10);
		setTextColor(console::Color::WHITE);
		setBackgroundColor(console::Color::BLACK);
		print("testing1");
		print("testing1") << "testing2: " << 2 << "\n";
		print("             testing3") << 3.1;

		console::Console::setBackgroundColor(console::Color::BLACK);
		for (int i = 0; i < 16; ++i) {
			console::Console::setTextColor((console::Color) i);
			console::Console::setCursorPosition(1, i);
			printColor((console::Color) i);
		}
		console::Console::setTextColor(console::Color::BLACK);
		for (int i = 0; i < 16; ++i) {
			console::Console::setBackgroundColor((console::Color) i);
			console::Console::setCursorPosition(1, 16 + i);
			printColor((console::Color) i);
		}

		console::Console::setTextColor(console::Color::BLACK);
		for (int i = 0; i < 16; ++i) {
			for (int j = 0; j < 16; ++j) {
				console::Console::setTextColor((console::Color) i);
				console::Console::setBackgroundColor((console::Color) j);
				console::Console::setCursorPosition(20 + 5* i, 16 + j);
				print("Test");
			}
		}

		int x, y;
		getCursorPosition(x,y);
	}

	void closePostLoop() override {
	}

private:
	void update(double deltaTime) override {
		console::sleep(1.0/60.0);
	}

	// Is called by the loop. Is called when ever a event occurs.
	void eventUpdate(console::ConsoleEvent& consoleEvent) override {
		switch (consoleEvent.type) {
			case console::ConsoleEventType::KEYDOWN:
				switch (consoleEvent.keyEvent.key) {
					case console::Key::ESCAPE:
						quit();
						break;
				}
				print("KEYDOWN");
				break;
			case console::ConsoleEventType::KEYUP:
				switch (consoleEvent.keyEvent.key) {
					case console::Key::RETURN:
						print("RETURN");
						break;
				}
				print("KEYUP");
				break;
			case console::ConsoleEventType::CONSOLERESIZE:
				clear();
				initPreLoop();
				break;
		}
	}

};

int main() {
	TestConsole testConsole;
	testConsole.startLoop();

	return 0;
}
<commit_msg>Updated test, can move a rec block around.<commit_after>#include <iostream>
#include <thread>
#include <sstream>

#include <console/console.h>

class Input {
public:
	Input() : left_(false), right_(false), up_(false), down_(false) {
	}

	bool left_;
	bool right_;
	bool up_;
	bool down_;
};

class TestConsole : public console::Console {
public:
	TestConsole() {
		x_ = 5;
		y_ = 5;
		xPos_ = 5;
		yPos_ = 5;
	}

protected:
	void printColor(console::Color color) {
		switch (color) {
			case console::Color::BLACK:
				print("BLACK");
				break;
			case console::Color::DARKBLUE:
				print("DARKBLUE");
				break;
			case console::Color::DARKGREEN:
				print("DARKGREEN");
				break;
			case console::Color::DARKCYAN:
				print("DARKCYAN");
				break;
			case console::Color::DARKRED:
				print("DARKRED");
				break;
			case console::Color::DARKMAGENTA:
				print("DARKMAGENTA");
				break;
			case console::Color::DARKYELLOW:
				print("DARKYELLOW");
				break;
			case console::Color::DARKGREY:
				print("DARKGREY");
				break;
			case console::Color::GREY:
				print("GREY");
				break;
			case console::Color::BLUE:
				print("BLUE");
				break;
			case console::Color::GREEN:
				print("GREEN");
				break;
			case console::Color::CYAN:
				print("CYAN");
				break;
			case console::Color::RED:
				print("RED");
				break;
			case console::Color::MAGENTA:
				print("MAGENTA");
				break;
			case console::Color::YELLOW:
				print("YELLOW");
				break;
			case console::Color::WHITE:
				print("WHITE");
				break;
		}
	}

	void initPreLoop() override {
		setTitle("Test");

		setCursorPosition(10, 10);
		setTextColor(console::Color::WHITE);
		setBackgroundColor(console::Color::BLACK);
		print("testing1");
		print("testing1") << "testing2: " << 2 << "\n";
		print("             testing3") << 3.1;

		console::Console::setBackgroundColor(console::Color::BLACK);
		for (int i = 0; i < 16; ++i) {
			console::Console::setTextColor((console::Color) i);
			console::Console::setCursorPosition(1, i);
			printColor((console::Color) i);
		}
		console::Console::setTextColor(console::Color::BLACK);
		for (int i = 0; i < 16; ++i) {
			console::Console::setBackgroundColor((console::Color) i);
			console::Console::setCursorPosition(1, 16 + i);
			printColor((console::Color) i);
		}

		console::Console::setTextColor(console::Color::BLACK);
		for (int i = 0; i < 16; ++i) {
			for (int j = 0; j < 16; ++j) {
				console::Console::setTextColor((console::Color) i);
				console::Console::setBackgroundColor((console::Color) j);
				console::Console::setCursorPosition(20 + 5* i, 16 + j);
				print("Test");
			}
		}

		int x, y;
		getCursorPosition(x,y);
	}

	void closePostLoop() override {
	}

private:
	double x_, y_;
	int xPos_, yPos_;

	Input input_;

	void update(double deltaTime) override {
		double step = 10 * deltaTime;

		if (y_ - 1 > 1 && input_.up_) {
			y_ -= step;
		}
		if (y_ + 1 < 100 && input_.down_) {
			y_ += step;
		}
		if (x_ - 1 > 1 && input_.left_) {
			x_ -= step;
		}
		if (x_ + 1 < 100 && input_.right_) {
			x_ += step;
		}

		if ((int) x_ != xPos_) {
			xPos_ = (int) x_;
			setCursorPosition(xPos_, yPos_);
			setTextColor(console::Color::WHITE);
			setBackgroundColor(console::Color::RED);
			print(" ");
		} else if ((int) y_ != yPos_) {
			yPos_ = (int) y_;
			setCursorPosition(xPos_, yPos_);
			setTextColor(console::Color::WHITE);
			setBackgroundColor(console::Color::RED);
			print(" ");
		}

		console::sleep(1.0/60.0);
	}

	// Is called by the loop. Is called when ever a event occurs.
	void eventUpdate(console::ConsoleEvent& consoleEvent) override {
		switch (consoleEvent.type) {
			case console::ConsoleEventType::KEYDOWN:
				switch (consoleEvent.keyEvent.key) {
					case console::Key::ESCAPE:
						quit();
						break;
					case console::Key::UP:
						input_.up_ = true;
						break;
					case console::Key::DOWN:
						input_.down_ = true;
						break;
					case console::Key::LEFT:
						input_.left_ = true;
						break;
					case console::Key::RIGHT:
						input_.right_ = true;
						break;
					case console::Key::KEY_0:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(20, 5);
						print("0");
						break;
					case console::Key::KEY_1:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(22, 5);
						print("1");
						break;
					case console::Key::KEY_2:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(24, 5);
						print("2");
						break;
					case console::Key::KEY_3:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(26, 5);
						print("3");
						break;
					case console::Key::KEY_4:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(28, 5);
						print("4");
						break;
					case console::Key::KEY_5:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(30, 5);
						print("5");
						break;
					case console::Key::KEY_6:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(32, 5);
						print("6");
						break;
					case console::Key::KEY_7:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(34, 5);
						print("7");
						break;
					case console::Key::KEY_8:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(36, 5);
						print("8");
						break;
					case console::Key::KEY_9:
						setTextColor(console::Color::WHITE);
						setBackgroundColor(console::Color::RED);
						setCursorPosition(38, 5);
						print("9");
						break;
				}

				break;
			case console::ConsoleEventType::KEYUP:
				switch (consoleEvent.keyEvent.key) {
					case console::Key::RETURN:
						print("RETURN");
						break;
					case console::Key::UP:
						input_.up_ = false;
						break;
					case console::Key::DOWN:
						input_.down_ = false;
						break;
					case console::Key::LEFT:
						input_.left_ = false;
						break;
					case console::Key::RIGHT:
						input_.right_ = false;
						break;
				}
				break;
			case console::ConsoleEventType::CONSOLERESIZE:
				clear();
				initPreLoop();
				break;
		}
	}

};

int main() {
	TestConsole testConsole;
	testConsole.startLoop();

	return 0;
}
<|endoftext|>


<commit_before>#ifndef OPENMVG_VOCABULARY_TREE_VOCABULARY_TREE_HPP
#define OPENMVG_VOCABULARY_TREE_VOCABULARY_TREE_HPP

#include "distance.hpp"
#include "feature_allocator.hpp"

#include <stdint.h>
#include <vector>
#include <cassert>
#include <limits>
#include <fstream>
#include <stdexcept>

namespace openMVG {
namespace voctree {

	typedef int32_t Word;

	/**
	 * \brief Optimized vocabulary tree quantizer, templated on feature type and distance metric
	 * for maximum efficiency.
	 *
	 * \c Feature is the data type of one feature. It has no requirements except compatibility with the distance metric.
	 *
	 * \c Distance is a functor that computes the distance between two Feature objects. It must have a \c result_type
	 * typedef specifying the type of the returned distance. For the purposes of VocabularyTree, this need not even be
	 * a metric; distances simply need to be comparable.
	 *
	 * \c FeatureAllocator is an STL-compatible allocator used to allocate Features internally.
	 */
	template<class Feature, class Distance = L2<Feature>,
	class FeatureAllocator = typename DefaultAllocator<Feature>::type>
	class VocabularyTree
	{
	public:
		/**
		 * \brief Constructor, empty tree.
		 *
		 * \param d Functor for computing the distance between two features
		 *
		 * @todo Allocator parameter, also in MutableVocabularyTree, TreeBuilder...
		 */
		VocabularyTree( Distance d = Distance( ) );

		/**
		 * \brief Constructor, loads vocabulary from file.
		 *
		 * \param file Saved vocabulary file
		 * \param d    Functor for computing the distance between two features
		 */
		VocabularyTree( const std::string& file, Distance d = Distance( ) );

		/// Quantizes a feature into a discrete word.
		Word quantize( const Feature& f ) const;

		/// Get the depth (number of levels) of the tree.
		uint32_t levels( ) const;
		/// Get the branching factor (max splits at each node) of the tree.
		uint32_t splits( ) const;
		/// Get the number of words the tree contains.
		uint32_t words( ) const;

		/// Clears vocabulary, leaving an empty tree.
		void clear( );

		/// Save vocabulary to a file.
		void save( const std::string& file ) const;
		/// Load vocabulary from a file.
		void load( const std::string& file );

	protected:
		typedef typename Distance::result_type distance_type;

		std::vector<Feature, FeatureAllocator> centers_;
		std::vector<uint8_t> valid_centers_; /// @todo Consider bit-vector
		Distance distance_;

		uint32_t k_; // splits, or branching factor
		uint32_t levels_;
		uint32_t num_words_; // number of leaf nodes
		uint32_t word_start_; // number of non-leaf nodes, or offset to the first leaf node

		bool initialized( ) const
		{
			return num_words_ != 0;
		}

		void setNodeCounts( );
	};

	template<class Feature, class Distance, class FeatureAllocator>
	VocabularyTree<Feature, Distance, FeatureAllocator>::VocabularyTree( Distance d )
	: distance_( d ), k_( 0 ), levels_( 0 ), num_words_( 0 ), word_start_( 0 ) { }

	template<class Feature, class Distance, class FeatureAllocator>
	VocabularyTree<Feature, Distance, FeatureAllocator>::VocabularyTree( const std::string& file, Distance d )
	: distance_( d ), k_( 0 ), levels_( 0 ), num_words_( 0 ), word_start_( 0 )
	{
		load( file );
	}

	template<class Feature, class Distance, class FeatureAllocator>
	Word VocabularyTree<Feature, Distance, FeatureAllocator>::quantize( const Feature& f ) const
	{
		//	printf("asserting\n");
		assert( initialized( ) );
		//	printf("initialized\n");
		int32_t index = -1; // virtual "root" index, which has no associated center.
		for ( unsigned level = 0; level < levels_; ++level )
		{
			// Calculate the offset to the first child of the current index.
			int32_t first_child = ( index + 1 ) * splits( );
			// Find the child center closest to the query.
			int32_t best_child = first_child;
			distance_type best_distance = std::numeric_limits<distance_type>::max( );
			for ( int32_t child = first_child; child < first_child + ( int32_t ) splits( ); ++child )
			{
				if ( !valid_centers_[child] )
					break; // Fewer than splits() children.
				distance_type child_distance = distance_( f, centers_[child] );
				if ( child_distance < best_distance )
				{
					best_child = child;
					best_distance = child_distance;
				}
			}
			index = best_child;
		}

		return index - word_start_;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	uint32_t VocabularyTree<Feature, Distance, FeatureAllocator>::levels( ) const
	{
		return levels_;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	uint32_t VocabularyTree<Feature, Distance, FeatureAllocator>::splits( ) const
	{
		return k_;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	uint32_t VocabularyTree<Feature, Distance, FeatureAllocator>::words( ) const
	{
		return num_words_;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	void VocabularyTree<Feature, Distance, FeatureAllocator>::clear( )
	{
		centers_.clear( );
		valid_centers_.clear( );
		k_ = levels_ = num_words_ = word_start_ = 0;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	void VocabularyTree<Feature, Distance, FeatureAllocator>::save( const std::string& file ) const
	{
		/// @todo Support serializing of non-"simple" feature classes
		/// @todo Some identifying name for the distance used
		assert( initialized( ) );

		std::ofstream out( file.c_str( ), std::ios_base::binary );
		out.write( ( char* ) ( &k_ ), sizeof (uint32_t ) );
		out.write( ( char* ) ( &levels_ ), sizeof (uint32_t ) );
		uint32_t size = centers_.size( );
		out.write( ( char* ) ( &size ), sizeof (uint32_t ) );
		out.write( ( char* ) ( &centers_[0] ), centers_.size( ) * sizeof (Feature ) );
		out.write( ( char* ) ( &valid_centers_[0] ), valid_centers_.size( ) );
	}

	template<class Feature, class Distance, class FeatureAllocator>
	void VocabularyTree<Feature, Distance, FeatureAllocator>::load( const std::string& file )
	{
		clear( );

		std::ifstream in;
		in.exceptions( std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit );

		uint32_t size;
		try
		{
			in.open( file.c_str( ), std::ios_base::binary );
			in.read( ( char* ) ( &k_ ), sizeof (uint32_t ) );
			in.read( ( char* ) ( &levels_ ), sizeof (uint32_t ) );
			in.read( ( char* ) ( &size ), sizeof (uint32_t ) );
			centers_.resize( size );
			valid_centers_.resize( size );
			in.read( ( char* ) ( &centers_[0] ), centers_.size( ) * sizeof (Feature ) );
			in.read( ( char* ) ( &valid_centers_[0] ), valid_centers_.size( ) );
		}
		catch ( std::ifstream::failure& e )
		{
			throw std::runtime_error(  "Failed to load vocabulary tree file" + file );
		}

		setNodeCounts( );
		assert( size == num_words_ + word_start_ );
	}

	template<class Feature, class Distance, class FeatureAllocator>
	void VocabularyTree<Feature, Distance, FeatureAllocator>::setNodeCounts( )
	{
		num_words_ = k_;
		word_start_ = 0;
		for ( uint32_t i = 0; i < levels_ - 1; ++i )
		{
			word_start_ += num_words_;
			num_words_ *= k_;
		}
	}

}
}
#endif //OPENMVG_VOCABULARY_TREE_VOCABULARY_TREE_HPP
<commit_msg>[voctree] add utility function to quantize a group of features<commit_after>#ifndef OPENMVG_VOCABULARY_TREE_VOCABULARY_TREE_HPP
#define OPENMVG_VOCABULARY_TREE_VOCABULARY_TREE_HPP

#include "distance.hpp"
#include "feature_allocator.hpp"

#include <stdint.h>
#include <vector>
#include <cassert>
#include <limits>
#include <fstream>
#include <stdexcept>

namespace openMVG {
namespace voctree {

	typedef int32_t Word;

	/**
	 * \brief Optimized vocabulary tree quantizer, templated on feature type and distance metric
	 * for maximum efficiency.
	 *
	 * \c Feature is the data type of one feature. It has no requirements except compatibility with the distance metric.
	 *
	 * \c Distance is a functor that computes the distance between two Feature objects. It must have a \c result_type
	 * typedef specifying the type of the returned distance. For the purposes of VocabularyTree, this need not even be
	 * a metric; distances simply need to be comparable.
	 *
	 * \c FeatureAllocator is an STL-compatible allocator used to allocate Features internally.
	 */
	template<class Feature, class Distance = L2<Feature>,
	class FeatureAllocator = typename DefaultAllocator<Feature>::type>
	class VocabularyTree
	{
	public:
		/**
		 * \brief Constructor, empty tree.
		 *
		 * \param d Functor for computing the distance between two features
		 *
		 * @todo Allocator parameter, also in MutableVocabularyTree, TreeBuilder...
		 */
		VocabularyTree( Distance d = Distance( ) );

		/**
		 * \brief Constructor, loads vocabulary from file.
		 *
		 * \param file Saved vocabulary file
		 * \param d    Functor for computing the distance between two features
		 */
		VocabularyTree( const std::string& file, Distance d = Distance( ) );

		/// Quantizes a feature into a discrete word.
		Word quantize( const Feature& feature ) const;

		/// Quantizes a set of features into visual words.
		std::vector<Word> quantize( const std::vector<Feature>& features ) const;

		/// Get the depth (number of levels) of the tree.
		uint32_t levels( ) const;
		/// Get the branching factor (max splits at each node) of the tree.
		uint32_t splits( ) const;
		/// Get the number of words the tree contains.
		uint32_t words( ) const;

		/// Clears vocabulary, leaving an empty tree.
		void clear( );

		/// Save vocabulary to a file.
		void save( const std::string& file ) const;
		/// Load vocabulary from a file.
		void load( const std::string& file );

	protected:
		typedef typename Distance::result_type distance_type;

		std::vector<Feature, FeatureAllocator> centers_;
		std::vector<uint8_t> valid_centers_; /// @todo Consider bit-vector
		Distance distance_;

		uint32_t k_; // splits, or branching factor
		uint32_t levels_;
		uint32_t num_words_; // number of leaf nodes
		uint32_t word_start_; // number of non-leaf nodes, or offset to the first leaf node

		bool initialized( ) const
		{
			return num_words_ != 0;
		}

		void setNodeCounts( );
	};

	template<class Feature, class Distance, class FeatureAllocator>
	VocabularyTree<Feature, Distance, FeatureAllocator>::VocabularyTree( Distance d )
	: distance_( d ), k_( 0 ), levels_( 0 ), num_words_( 0 ), word_start_( 0 ) { }

	template<class Feature, class Distance, class FeatureAllocator>
	VocabularyTree<Feature, Distance, FeatureAllocator>::VocabularyTree( const std::string& file, Distance d )
	: distance_( d ), k_( 0 ), levels_( 0 ), num_words_( 0 ), word_start_( 0 )
	{
		load( file );
	}

	template<class Feature, class Distance, class FeatureAllocator>
	Word VocabularyTree<Feature, Distance, FeatureAllocator>::quantize( const Feature& feature ) const
	{
		//	printf("asserting\n");
		assert( initialized( ) );
		//	printf("initialized\n");
		int32_t index = -1; // virtual "root" index, which has no associated center.
		for ( unsigned level = 0; level < levels_; ++level )
		{
			// Calculate the offset to the first child of the current index.
			int32_t first_child = ( index + 1 ) * splits( );
			// Find the child center closest to the query.
			int32_t best_child = first_child;
			distance_type best_distance = std::numeric_limits<distance_type>::max( );
			for ( int32_t child = first_child; child < first_child + ( int32_t ) splits( ); ++child )
			{
				if ( !valid_centers_[child] )
					break; // Fewer than splits() children.
				distance_type child_distance = distance_( feature, centers_[child] );
				if ( child_distance < best_distance )
				{
					best_child = child;
					best_distance = child_distance;
				}
			}
			index = best_child;
		}

		return index - word_start_;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	std::vector<Word> VocabularyTree<Feature, Distance, FeatureAllocator>::quantize( const std::vector<Feature>& features ) const
	{
		std::vector<Word> imgVisualWords(features.size(), 0);

		// quantize the features
		#pragma omp parallel for
		for( size_t j = 0; j < features.size(); ++j )
		{
			// store the visual word associated to the feature in the temporary list
			imgVisualWords[j] = quantize(features[j]);
		}

		// add the vector to the documents
		return imgVisualWords;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	uint32_t VocabularyTree<Feature, Distance, FeatureAllocator>::levels( ) const
	{
		return levels_;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	uint32_t VocabularyTree<Feature, Distance, FeatureAllocator>::splits( ) const
	{
		return k_;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	uint32_t VocabularyTree<Feature, Distance, FeatureAllocator>::words( ) const
	{
		return num_words_;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	void VocabularyTree<Feature, Distance, FeatureAllocator>::clear( )
	{
		centers_.clear( );
		valid_centers_.clear( );
		k_ = levels_ = num_words_ = word_start_ = 0;
	}

	template<class Feature, class Distance, class FeatureAllocator>
	void VocabularyTree<Feature, Distance, FeatureAllocator>::save( const std::string& file ) const
	{
		/// @todo Support serializing of non-"simple" feature classes
		/// @todo Some identifying name for the distance used
		assert( initialized( ) );

		std::ofstream out( file.c_str( ), std::ios_base::binary );
		out.write( ( char* ) ( &k_ ), sizeof (uint32_t ) );
		out.write( ( char* ) ( &levels_ ), sizeof (uint32_t ) );
		uint32_t size = centers_.size( );
		out.write( ( char* ) ( &size ), sizeof (uint32_t ) );
		out.write( ( char* ) ( &centers_[0] ), centers_.size( ) * sizeof (Feature ) );
		out.write( ( char* ) ( &valid_centers_[0] ), valid_centers_.size( ) );
	}

	template<class Feature, class Distance, class FeatureAllocator>
	void VocabularyTree<Feature, Distance, FeatureAllocator>::load( const std::string& file )
	{
		clear( );

		std::ifstream in;
		in.exceptions( std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit );

		uint32_t size;
		try
		{
			in.open( file.c_str( ), std::ios_base::binary );
			in.read( ( char* ) ( &k_ ), sizeof (uint32_t ) );
			in.read( ( char* ) ( &levels_ ), sizeof (uint32_t ) );
			in.read( ( char* ) ( &size ), sizeof (uint32_t ) );
			centers_.resize( size );
			valid_centers_.resize( size );
			in.read( ( char* ) ( &centers_[0] ), centers_.size( ) * sizeof (Feature ) );
			in.read( ( char* ) ( &valid_centers_[0] ), valid_centers_.size( ) );
		}
		catch ( std::ifstream::failure& e )
		{
			throw std::runtime_error(  "Failed to load vocabulary tree file" + file );
		}

		setNodeCounts( );
		assert( size == num_words_ + word_start_ );
	}

	template<class Feature, class Distance, class FeatureAllocator>
	void VocabularyTree<Feature, Distance, FeatureAllocator>::setNodeCounts( )
	{
		num_words_ = k_;
		word_start_ = 0;
		for ( uint32_t i = 0; i < levels_ - 1; ++i )
		{
			word_start_ += num_words_;
			num_words_ *= k_;
		}
	}

}
}
#endif //OPENMVG_VOCABULARY_TREE_VOCABULARY_TREE_HPP
<|endoftext|>