<commit_before>//===--- JSONCompilationDatabase.cpp - ------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file contains the implementation of the JSONCompilationDatabase.
//
//===----------------------------------------------------------------------===//

#include "clang/Tooling/JSONCompilationDatabase.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/system_error.h"

namespace clang {
namespace tooling {

namespace {

/// \brief A parser for escaped strings of command line arguments.
///
/// Assumes \-escaping for quoted arguments (see the documentation of
/// unescapeCommandLine(...)).
class CommandLineArgumentParser {
 public:
  CommandLineArgumentParser(StringRef CommandLine)
      : Input(CommandLine), Position(Input.begin()-1) {}

  std::vector<std::string> parse() {
    bool HasMoreInput = true;
    while (HasMoreInput && nextNonWhitespace()) {
      std::string Argument;
      HasMoreInput = parseStringInto(Argument);
      CommandLine.push_back(Argument);
    }
    return CommandLine;
  }

 private:
  // All private methods return true if there is more input available.

  bool parseStringInto(std::string &String) {
    do {
      if (*Position == '"') {
        if (!parseDoubleQuotedStringInto(String)) return false;
      } else if (*Position == '\'') {
        if (!parseSingleQuotedStringInto(String)) return false;
      } else {
        if (!parseFreeStringInto(String)) return false;
      }
    } while (*Position != ' ');
    return true;
  }

  bool parseDoubleQuotedStringInto(std::string &String) {
    if (!next()) return false;
    while (*Position != '"') {
      if (!skipEscapeCharacter()) return false;
      String.push_back(*Position);
      if (!next()) return false;
    }
    return next();
  }

  bool parseSingleQuotedStringInto(std::string &String) {
    if (!next()) return false;
    while (*Position != '\'') {
      String.push_back(*Position);
      if (!next()) return false;
    }
    return next();
  }

  bool parseFreeStringInto(std::string &String) {
    do {
      if (!skipEscapeCharacter()) return false;
      String.push_back(*Position);
      if (!next()) return false;
    } while (*Position != ' ' && *Position != '"' && *Position != '\'');
    return true;
  }

  bool skipEscapeCharacter() {
    if (*Position == '\\') {
      return next();
    }
    return true;
  }

  bool nextNonWhitespace() {
    do {
      if (!next()) return false;
    } while (*Position == ' ');
    return true;
  }

  bool next() {
    ++Position;
    return Position != Input.end();
  }

  const StringRef Input;
  StringRef::iterator Position;
  std::vector<std::string> CommandLine;
};

std::vector<std::string> unescapeCommandLine(
    StringRef EscapedCommandLine) {
  CommandLineArgumentParser parser(EscapedCommandLine);
  return parser.parse();
}

} // end namespace

class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
  virtual CompilationDatabase *loadFromDirectory(
      StringRef Directory, std::string &ErrorMessage) {
    SmallString<1024> JSONDatabasePath(Directory);
    llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
    OwningPtr<CompilationDatabase> Database(
        JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
    if (!Database)
      return NULL;
    return Database.take();
  }
};

// Register the JSONCompilationDatabasePlugin with the
// CompilationDatabasePluginRegistry using this statically initialized variable.
static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
X("json-compilation-database", "Reads JSON formatted compilation databases");

// This anchor is used to force the linker to link in the generated object file
// and thus register the JSONCompilationDatabasePlugin.
volatile int JSONAnchorSource = 0;

JSONCompilationDatabase *
JSONCompilationDatabase::loadFromFile(StringRef FilePath,
                                      std::string &ErrorMessage) {
  OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
  llvm::error_code Result =
    llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
  if (Result != 0) {
    ErrorMessage = "Error while opening JSON database: " + Result.message();
    return NULL;
  }
  OwningPtr<JSONCompilationDatabase> Database(
    new JSONCompilationDatabase(DatabaseBuffer.take()));
  if (!Database->parse(ErrorMessage))
    return NULL;
  return Database.take();
}

JSONCompilationDatabase *
JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
                                        std::string &ErrorMessage) {
  OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
      llvm::MemoryBuffer::getMemBuffer(DatabaseString));
  OwningPtr<JSONCompilationDatabase> Database(
      new JSONCompilationDatabase(DatabaseBuffer.take()));
  if (!Database->parse(ErrorMessage))
    return NULL;
  return Database.take();
}

std::vector<CompileCommand>
JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
  SmallString<128> NativeFilePath;
  llvm::sys::path::native(FilePath, NativeFilePath);
  std::vector<StringRef> PossibleMatches;
  std::string Error;
  llvm::raw_string_ostream ES(Error);
  StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
  if (Match.empty())
    return std::vector<CompileCommand>();
  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
    CommandsRefI = IndexByFile.find(Match);
  if (CommandsRefI == IndexByFile.end())
    return std::vector<CompileCommand>();
  std::vector<CompileCommand> Commands;
  getCommands(CommandsRefI->getValue(), Commands);
  return Commands;
}

std::vector<std::string>
JSONCompilationDatabase::getAllFiles() const {
  std::vector<std::string> Result;

  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
    CommandsRefI = IndexByFile.begin();
  const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
    CommandsRefEnd = IndexByFile.end();
  for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
    Result.push_back(CommandsRefI->first().str());
  }

  return Result;
}

std::vector<CompileCommand>
JSONCompilationDatabase::getAllCompileCommands() const {
  std::vector<CompileCommand> Commands;
  for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
        CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
      CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
    getCommands(CommandsRefI->getValue(), Commands);
  }
  return Commands;
}

void JSONCompilationDatabase::getCommands(
                                  ArrayRef<CompileCommandRef> CommandsRef,
                                  std::vector<CompileCommand> &Commands) const {
  for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
    SmallString<8> DirectoryStorage;
    SmallString<1024> CommandStorage;
    Commands.push_back(CompileCommand(
      // FIXME: Escape correctly:
      CommandsRef[I].first->getValue(DirectoryStorage),
      unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
  }
}

bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
  llvm::yaml::document_iterator I = YAMLStream.begin();
  if (I == YAMLStream.end()) {
    ErrorMessage = "Error while parsing YAML.";
    return false;
  }
  llvm::yaml::Node *Root = I->getRoot();
  if (Root == NULL) {
    ErrorMessage = "Error while parsing YAML.";
    return false;
  }
  llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
  if (Array == NULL) {
    ErrorMessage = "Expected array.";
    return false;
  }
  for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
                                          AE = Array->end();
       AI != AE; ++AI) {
    llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
    if (Object == NULL) {
      ErrorMessage = "Expected object.";
      return false;
    }
    llvm::yaml::ScalarNode *Directory = NULL;
    llvm::yaml::ScalarNode *Command = NULL;
    llvm::yaml::ScalarNode *File = NULL;
    for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
                                           KVE = Object->end();
         KVI != KVE; ++KVI) {
      llvm::yaml::Node *Value = (*KVI).getValue();
      if (Value == NULL) {
        ErrorMessage = "Expected value.";
        return false;
      }
      llvm::yaml::ScalarNode *ValueString =
          dyn_cast<llvm::yaml::ScalarNode>(Value);
      if (ValueString == NULL) {
        ErrorMessage = "Expected string as value.";
        return false;
      }
      llvm::yaml::ScalarNode *KeyString =
          dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
      if (KeyString == NULL) {
        ErrorMessage = "Expected strings as key.";
        return false;
      }
      SmallString<8> KeyStorage;
      if (KeyString->getValue(KeyStorage) == "directory") {
        Directory = ValueString;
      } else if (KeyString->getValue(KeyStorage) == "command") {
        Command = ValueString;
      } else if (KeyString->getValue(KeyStorage) == "file") {
        File = ValueString;
      } else {
        ErrorMessage = ("Unknown key: \"" +
                        KeyString->getRawValue() + "\"").str();
        return false;
      }
    }
    if (!File) {
      ErrorMessage = "Missing key: \"file\".";
      return false;
    }
    if (!Command) {
      ErrorMessage = "Missing key: \"command\".";
      return false;
    }
    if (!Directory) {
      ErrorMessage = "Missing key: \"directory\".";
      return false;
    }
    SmallString<8> FileStorage;
    StringRef FileName = File->getValue(FileStorage);
    SmallString<128> NativeFilePath;
    if (llvm::sys::path::is_relative(FileName)) {
      SmallString<8> DirectoryStorage;
      SmallString<128> AbsolutePath(
          Directory->getValue(DirectoryStorage));
      llvm::sys::path::append(AbsolutePath, FileName);
      llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
    } else {
      llvm::sys::path::native(FileName, NativeFilePath);
    }
    IndexByFile[NativeFilePath].push_back(
        CompileCommandRef(Directory, Command));
    MatchTrie.insert(NativeFilePath.str());
  }
  return true;
}

} // end namespace tooling
} // end namespace clang
<commit_msg>Put helper class in anonymous namespace.<commit_after>//===--- JSONCompilationDatabase.cpp - ------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file contains the implementation of the JSONCompilationDatabase.
//
//===----------------------------------------------------------------------===//

#include "clang/Tooling/JSONCompilationDatabase.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/system_error.h"

namespace clang {
namespace tooling {

namespace {

/// \brief A parser for escaped strings of command line arguments.
///
/// Assumes \-escaping for quoted arguments (see the documentation of
/// unescapeCommandLine(...)).
class CommandLineArgumentParser {
 public:
  CommandLineArgumentParser(StringRef CommandLine)
      : Input(CommandLine), Position(Input.begin()-1) {}

  std::vector<std::string> parse() {
    bool HasMoreInput = true;
    while (HasMoreInput && nextNonWhitespace()) {
      std::string Argument;
      HasMoreInput = parseStringInto(Argument);
      CommandLine.push_back(Argument);
    }
    return CommandLine;
  }

 private:
  // All private methods return true if there is more input available.

  bool parseStringInto(std::string &String) {
    do {
      if (*Position == '"') {
        if (!parseDoubleQuotedStringInto(String)) return false;
      } else if (*Position == '\'') {
        if (!parseSingleQuotedStringInto(String)) return false;
      } else {
        if (!parseFreeStringInto(String)) return false;
      }
    } while (*Position != ' ');
    return true;
  }

  bool parseDoubleQuotedStringInto(std::string &String) {
    if (!next()) return false;
    while (*Position != '"') {
      if (!skipEscapeCharacter()) return false;
      String.push_back(*Position);
      if (!next()) return false;
    }
    return next();
  }

  bool parseSingleQuotedStringInto(std::string &String) {
    if (!next()) return false;
    while (*Position != '\'') {
      String.push_back(*Position);
      if (!next()) return false;
    }
    return next();
  }

  bool parseFreeStringInto(std::string &String) {
    do {
      if (!skipEscapeCharacter()) return false;
      String.push_back(*Position);
      if (!next()) return false;
    } while (*Position != ' ' && *Position != '"' && *Position != '\'');
    return true;
  }

  bool skipEscapeCharacter() {
    if (*Position == '\\') {
      return next();
    }
    return true;
  }

  bool nextNonWhitespace() {
    do {
      if (!next()) return false;
    } while (*Position == ' ');
    return true;
  }

  bool next() {
    ++Position;
    return Position != Input.end();
  }

  const StringRef Input;
  StringRef::iterator Position;
  std::vector<std::string> CommandLine;
};

std::vector<std::string> unescapeCommandLine(
    StringRef EscapedCommandLine) {
  CommandLineArgumentParser parser(EscapedCommandLine);
  return parser.parse();
}

class JSONCompilationDatabasePlugin : public CompilationDatabasePlugin {
  virtual CompilationDatabase *loadFromDirectory(
      StringRef Directory, std::string &ErrorMessage) {
    SmallString<1024> JSONDatabasePath(Directory);
    llvm::sys::path::append(JSONDatabasePath, "compile_commands.json");
    OwningPtr<CompilationDatabase> Database(
        JSONCompilationDatabase::loadFromFile(JSONDatabasePath, ErrorMessage));
    if (!Database)
      return NULL;
    return Database.take();
  }
};

} // end namespace

// Register the JSONCompilationDatabasePlugin with the
// CompilationDatabasePluginRegistry using this statically initialized variable.
static CompilationDatabasePluginRegistry::Add<JSONCompilationDatabasePlugin>
X("json-compilation-database", "Reads JSON formatted compilation databases");

// This anchor is used to force the linker to link in the generated object file
// and thus register the JSONCompilationDatabasePlugin.
volatile int JSONAnchorSource = 0;

JSONCompilationDatabase *
JSONCompilationDatabase::loadFromFile(StringRef FilePath,
                                      std::string &ErrorMessage) {
  OwningPtr<llvm::MemoryBuffer> DatabaseBuffer;
  llvm::error_code Result =
    llvm::MemoryBuffer::getFile(FilePath, DatabaseBuffer);
  if (Result != 0) {
    ErrorMessage = "Error while opening JSON database: " + Result.message();
    return NULL;
  }
  OwningPtr<JSONCompilationDatabase> Database(
    new JSONCompilationDatabase(DatabaseBuffer.take()));
  if (!Database->parse(ErrorMessage))
    return NULL;
  return Database.take();
}

JSONCompilationDatabase *
JSONCompilationDatabase::loadFromBuffer(StringRef DatabaseString,
                                        std::string &ErrorMessage) {
  OwningPtr<llvm::MemoryBuffer> DatabaseBuffer(
      llvm::MemoryBuffer::getMemBuffer(DatabaseString));
  OwningPtr<JSONCompilationDatabase> Database(
      new JSONCompilationDatabase(DatabaseBuffer.take()));
  if (!Database->parse(ErrorMessage))
    return NULL;
  return Database.take();
}

std::vector<CompileCommand>
JSONCompilationDatabase::getCompileCommands(StringRef FilePath) const {
  SmallString<128> NativeFilePath;
  llvm::sys::path::native(FilePath, NativeFilePath);
  std::vector<StringRef> PossibleMatches;
  std::string Error;
  llvm::raw_string_ostream ES(Error);
  StringRef Match = MatchTrie.findEquivalent(NativeFilePath.str(), ES);
  if (Match.empty())
    return std::vector<CompileCommand>();
  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
    CommandsRefI = IndexByFile.find(Match);
  if (CommandsRefI == IndexByFile.end())
    return std::vector<CompileCommand>();
  std::vector<CompileCommand> Commands;
  getCommands(CommandsRefI->getValue(), Commands);
  return Commands;
}

std::vector<std::string>
JSONCompilationDatabase::getAllFiles() const {
  std::vector<std::string> Result;

  llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
    CommandsRefI = IndexByFile.begin();
  const llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
    CommandsRefEnd = IndexByFile.end();
  for (; CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
    Result.push_back(CommandsRefI->first().str());
  }

  return Result;
}

std::vector<CompileCommand>
JSONCompilationDatabase::getAllCompileCommands() const {
  std::vector<CompileCommand> Commands;
  for (llvm::StringMap< std::vector<CompileCommandRef> >::const_iterator
        CommandsRefI = IndexByFile.begin(), CommandsRefEnd = IndexByFile.end();
      CommandsRefI != CommandsRefEnd; ++CommandsRefI) {
    getCommands(CommandsRefI->getValue(), Commands);
  }
  return Commands;
}

void JSONCompilationDatabase::getCommands(
                                  ArrayRef<CompileCommandRef> CommandsRef,
                                  std::vector<CompileCommand> &Commands) const {
  for (int I = 0, E = CommandsRef.size(); I != E; ++I) {
    SmallString<8> DirectoryStorage;
    SmallString<1024> CommandStorage;
    Commands.push_back(CompileCommand(
      // FIXME: Escape correctly:
      CommandsRef[I].first->getValue(DirectoryStorage),
      unescapeCommandLine(CommandsRef[I].second->getValue(CommandStorage))));
  }
}

bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
  llvm::yaml::document_iterator I = YAMLStream.begin();
  if (I == YAMLStream.end()) {
    ErrorMessage = "Error while parsing YAML.";
    return false;
  }
  llvm::yaml::Node *Root = I->getRoot();
  if (Root == NULL) {
    ErrorMessage = "Error while parsing YAML.";
    return false;
  }
  llvm::yaml::SequenceNode *Array = dyn_cast<llvm::yaml::SequenceNode>(Root);
  if (Array == NULL) {
    ErrorMessage = "Expected array.";
    return false;
  }
  for (llvm::yaml::SequenceNode::iterator AI = Array->begin(),
                                          AE = Array->end();
       AI != AE; ++AI) {
    llvm::yaml::MappingNode *Object = dyn_cast<llvm::yaml::MappingNode>(&*AI);
    if (Object == NULL) {
      ErrorMessage = "Expected object.";
      return false;
    }
    llvm::yaml::ScalarNode *Directory = NULL;
    llvm::yaml::ScalarNode *Command = NULL;
    llvm::yaml::ScalarNode *File = NULL;
    for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
                                           KVE = Object->end();
         KVI != KVE; ++KVI) {
      llvm::yaml::Node *Value = (*KVI).getValue();
      if (Value == NULL) {
        ErrorMessage = "Expected value.";
        return false;
      }
      llvm::yaml::ScalarNode *ValueString =
          dyn_cast<llvm::yaml::ScalarNode>(Value);
      if (ValueString == NULL) {
        ErrorMessage = "Expected string as value.";
        return false;
      }
      llvm::yaml::ScalarNode *KeyString =
          dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
      if (KeyString == NULL) {
        ErrorMessage = "Expected strings as key.";
        return false;
      }
      SmallString<8> KeyStorage;
      if (KeyString->getValue(KeyStorage) == "directory") {
        Directory = ValueString;
      } else if (KeyString->getValue(KeyStorage) == "command") {
        Command = ValueString;
      } else if (KeyString->getValue(KeyStorage) == "file") {
        File = ValueString;
      } else {
        ErrorMessage = ("Unknown key: \"" +
                        KeyString->getRawValue() + "\"").str();
        return false;
      }
    }
    if (!File) {
      ErrorMessage = "Missing key: \"file\".";
      return false;
    }
    if (!Command) {
      ErrorMessage = "Missing key: \"command\".";
      return false;
    }
    if (!Directory) {
      ErrorMessage = "Missing key: \"directory\".";
      return false;
    }
    SmallString<8> FileStorage;
    StringRef FileName = File->getValue(FileStorage);
    SmallString<128> NativeFilePath;
    if (llvm::sys::path::is_relative(FileName)) {
      SmallString<8> DirectoryStorage;
      SmallString<128> AbsolutePath(
          Directory->getValue(DirectoryStorage));
      llvm::sys::path::append(AbsolutePath, FileName);
      llvm::sys::path::native(AbsolutePath.str(), NativeFilePath);
    } else {
      llvm::sys::path::native(FileName, NativeFilePath);
    }
    IndexByFile[NativeFilePath].push_back(
        CompileCommandRef(Directory, Command));
    MatchTrie.insert(NativeFilePath.str());
  }
  return true;
}

} // end namespace tooling
} // end namespace clang
<|endoftext|>


<commit_before>#include "config.h"

#include <iostream>

#include <memcached/engine.h>
#include <memcached/protocol_binary.h>

#include "histo.hh"

namespace STATWRITER_NAMESPACE {

void add_casted_stat(const char *k, const char *v,
                            ADD_STAT add_stat, const void *cookie) {
    add_stat(k, static_cast<uint16_t>(strlen(k)),
             v, static_cast<uint32_t>(strlen(v)), cookie);
}

template <typename T>
void add_casted_stat(const char *k, T v,
                            ADD_STAT add_stat, const void *cookie) {
    std::stringstream vals;
    vals << v;
    add_casted_stat(k, vals.str().c_str(), add_stat, cookie);
}

template <typename T>
void add_casted_stat(const char *k, const Atomic<T> &v,
                            ADD_STAT add_stat, const void *cookie) {
    add_casted_stat(k, v.get(), add_stat, cookie);
}

/// @cond DETAILS
/**
 * Convert a histogram into a bunch of calls to add stats.
 */
template <typename T>
struct histo_stat_adder {
    histo_stat_adder(const char *k, ADD_STAT a, const void *c)
        : prefix(k), add_stat(a), cookie(c) {}
    void operator() (const HistogramBin<T>* b) {
        if (b->count()) {
            std::stringstream ss;
            ss << prefix << "_" << b->start() << "," << b->end();
            add_casted_stat(ss.str().c_str(), b->count(), add_stat, cookie);
        }
    }
    const char *prefix;
    ADD_STAT add_stat;
    const void *cookie;
};
/// @endcond

template <typename T>
void add_casted_stat(const char *k, const Histogram<T> &v,
                            ADD_STAT add_stat, const void *cookie) {
    histo_stat_adder<T> a(k, add_stat, cookie);
    std::for_each(v.begin(), v.end(), a);
}

template <typename P, typename T>
void add_prefixed_stat(P prefix, const char *nm, T val,
                  ADD_STAT add_stat, const void *cookie) {
    std::stringstream name;
    name << prefix << ":" << nm;

    add_casted_stat(name.str().c_str(), val, add_stat, cookie);
}

template <typename P, typename T>
void add_prefixed_stat(P prefix, const char *nm, Histogram<T> &val,
                  ADD_STAT add_stat, const void *cookie) {
    std::stringstream name;
    name << prefix << ":" << nm;

    add_casted_stat(name.str().c_str(), val, add_stat, cookie);
}

}

using namespace STATWRITER_NAMESPACE;
<commit_msg>declare base add_casted_stat overload as inline and unbreak build<commit_after>#include "config.h"

#include <iostream>

#include <memcached/engine.h>
#include <memcached/protocol_binary.h>

#include "histo.hh"

namespace STATWRITER_NAMESPACE {

inline void add_casted_stat(const char *k, const char *v,
                            ADD_STAT add_stat, const void *cookie) {
    add_stat(k, static_cast<uint16_t>(strlen(k)),
             v, static_cast<uint32_t>(strlen(v)), cookie);
}

template <typename T>
void add_casted_stat(const char *k, T v,
                            ADD_STAT add_stat, const void *cookie) {
    std::stringstream vals;
    vals << v;
    add_casted_stat(k, vals.str().c_str(), add_stat, cookie);
}

template <typename T>
void add_casted_stat(const char *k, const Atomic<T> &v,
                            ADD_STAT add_stat, const void *cookie) {
    add_casted_stat(k, v.get(), add_stat, cookie);
}

/// @cond DETAILS
/**
 * Convert a histogram into a bunch of calls to add stats.
 */
template <typename T>
struct histo_stat_adder {
    histo_stat_adder(const char *k, ADD_STAT a, const void *c)
        : prefix(k), add_stat(a), cookie(c) {}
    void operator() (const HistogramBin<T>* b) {
        if (b->count()) {
            std::stringstream ss;
            ss << prefix << "_" << b->start() << "," << b->end();
            add_casted_stat(ss.str().c_str(), b->count(), add_stat, cookie);
        }
    }
    const char *prefix;
    ADD_STAT add_stat;
    const void *cookie;
};
/// @endcond

template <typename T>
void add_casted_stat(const char *k, const Histogram<T> &v,
                            ADD_STAT add_stat, const void *cookie) {
    histo_stat_adder<T> a(k, add_stat, cookie);
    std::for_each(v.begin(), v.end(), a);
}

template <typename P, typename T>
void add_prefixed_stat(P prefix, const char *nm, T val,
                  ADD_STAT add_stat, const void *cookie) {
    std::stringstream name;
    name << prefix << ":" << nm;

    add_casted_stat(name.str().c_str(), val, add_stat, cookie);
}

template <typename P, typename T>
void add_prefixed_stat(P prefix, const char *nm, Histogram<T> &val,
                  ADD_STAT add_stat, const void *cookie) {
    std::stringstream name;
    name << prefix << ":" << nm;

    add_casted_stat(name.str().c_str(), val, add_stat, cookie);
}

}

using namespace STATWRITER_NAMESPACE;
<|endoftext|>


<commit_before>#include <common/buffer.h>
#include <common/endian.h>

#include <event/action.h>
#include <event/callback.h>
#include <event/event_system.h>

#include <io/pipe.h>
#include <io/pipe_link.h>
#include <io/pipe_null.h>
#include <io/socket.h>
#include <io/splice.h>
#include <io/splice_pair.h>

#include <net/tcp_client.h>

#include "proxy_client.h"

ProxyClient::ProxyClient(XCodec *local_codec, XCodec *remote_codec,
			 Socket *local_socket, SocketAddressFamily family,
			 const std::string& remote_name)
: log_("/wanproxy/proxy/client"),
  stop_action_(NULL),
  local_action_(NULL),
  local_codec_(local_codec),
  local_socket_(local_socket),
  remote_action_(NULL),
  remote_codec_(remote_codec),
  remote_socket_(NULL),
  pipes_(),
  incoming_splice_(NULL),
  outgoing_splice_(NULL),
  splice_pair_(NULL),
  splice_action_(NULL)
{
	EventCallback *cb = callback(this, &ProxyClient::connect_complete);
	remote_action_ = TCPClient::connect(&remote_socket_, family, remote_name, cb);
	if (remote_action_ == NULL) {
		ASSERT(remote_socket_ == NULL);

		ERROR(log_) << "Could not connect to: " << remote_name;

		EventCallback *lcb = callback(this, &ProxyClient::close_complete,
					      (void *)local_socket_);
		local_action_ = local_socket_->close(lcb);
		return;
	}

	Callback *scb = callback(this, &ProxyClient::stop);
	stop_action_ = EventSystem::instance()->register_interest(EventInterestStop, scb);
}

ProxyClient::~ProxyClient()
{
	ASSERT(stop_action_ == NULL);
	ASSERT(local_action_ == NULL);
	ASSERT(local_socket_ == NULL);
	ASSERT(remote_action_ == NULL);
	ASSERT(remote_socket_ == NULL);
	ASSERT(incoming_splice_ == NULL);
	ASSERT(outgoing_splice_ == NULL);
	ASSERT(splice_pair_ == NULL);
	ASSERT(splice_action_ == NULL);

	std::set<Pipe *>::iterator it;
	while ((it = pipes_.begin()) != pipes_.end()) {
		Pipe *pipe = *it;
		pipes_.erase(it);

		delete pipe;
	}
}

void
ProxyClient::close_complete(Event e, void *channel)
{
	if (channel == (void *)local_socket_) {
		local_action_->cancel();
		local_action_ = NULL;
	}

	if (channel == (void *)remote_socket_) {
		remote_action_->cancel();
		remote_action_ = NULL;
	}

	switch (e.type_) {
	case Event::Done:
		break;
	default:
		/* XXX Never sure what to do here.  */
		ERROR(log_) << "Unexpected event: " << e;
		return;
	}

	if (channel == (void *)local_socket_) {
		ASSERT(local_socket_ != NULL);
		delete local_socket_;
		local_socket_ = NULL;
	}

	if (channel == (void *)remote_socket_) {
		ASSERT(remote_socket_ != NULL);
		delete remote_socket_;
		remote_socket_ = NULL;
	}

	if (local_socket_ == NULL && remote_socket_ == NULL) {
		delete this;
	}
}

void
ProxyClient::connect_complete(Event e)
{
	remote_action_->cancel();
	remote_action_ = NULL;

	switch (e.type_) {
	case Event::Done:
		break;
	case Event::Error:
		INFO(log_) << "Connect failed: " << e;
		schedule_close();
		return;
	default:
		ERROR(log_) << "Unexpected event: " << e;
		schedule_close();
		return;
	}

	Pipe *incoming_left = new PipeNull();
	pipes_.insert(incoming_left);

	Pipe *incoming_right = new PipeNull();
	pipes_.insert(incoming_right);

	Pipe *incoming_link = new PipeLink(incoming_left, incoming_right);
	pipes_.insert(incoming_link);

	incoming_splice_ = new Splice(remote_socket_, incoming_link, local_socket_);

	Pipe *outgoing_left = new PipeNull();
	pipes_.insert(outgoing_left);

	Pipe *outgoing_right = new PipeNull();
	pipes_.insert(outgoing_right);

	Pipe *outgoing_link = new PipeLink(outgoing_left, outgoing_right);
	pipes_.insert(outgoing_link);

	outgoing_splice_ = new Splice(local_socket_, outgoing_link, remote_socket_);

	splice_pair_ = new SplicePair(outgoing_splice_, incoming_splice_);

	EventCallback *cb = callback(this, &ProxyClient::splice_complete);
	splice_action_ = splice_pair_->start(cb);
}

void
ProxyClient::splice_complete(Event e)
{
	splice_action_->cancel();
	splice_action_ = NULL;

	delete splice_pair_;
	splice_pair_ = NULL;

	delete outgoing_splice_;
	outgoing_splice_ = NULL;

	delete incoming_splice_;
	incoming_splice_ = NULL;

	switch (e.type_) {
	case Event::Done:
		break;
	default:
		ERROR(log_) << "Unexpected event: " << e;
		break;
	}

	schedule_close();
}

void
ProxyClient::stop(void)
{
	stop_action_->cancel();
	stop_action_ = NULL;

	/*
	 * Connecting.
	 */
	if (local_action_ == NULL && remote_action_ != NULL &&
	    splice_action_ == NULL) {
		remote_action_->cancel();
		remote_action_ = NULL;

		schedule_close();
		return;
	}

	/*
	 * Already closing.  Should not happen.
	 */
	if (local_action_ != NULL || remote_action_ != NULL) {
		HALT(log_) << "Client already closing during stop.";
		return;
	}

	schedule_close();
}

void
ProxyClient::schedule_close(void)
{
	if (stop_action_ != NULL) {
		stop_action_->cancel();
		stop_action_ = NULL;
	}

	if (splice_pair_ != NULL) {
		if (splice_action_ != NULL) {
			splice_action_->cancel();
			splice_action_ = NULL;
		}

		ASSERT(outgoing_splice_ != NULL);
		ASSERT(incoming_splice_ != NULL);

		delete splice_pair_;
		splice_pair_ = NULL;

		delete outgoing_splice_;
		outgoing_splice_ = NULL;

		delete incoming_splice_;
		incoming_splice_ = NULL;
	}

	ASSERT(local_action_ == NULL);
	ASSERT(local_socket_ != NULL);
	EventCallback *lcb = callback(this, &ProxyClient::close_complete,
				      (void *)local_socket_);
	local_action_ = local_socket_->close(lcb);

	ASSERT(remote_action_ == NULL);
	ASSERT(remote_socket_ != NULL);
	EventCallback *rcb = callback(this, &ProxyClient::close_complete,
				      (void *)remote_socket_);
	remote_action_ = remote_socket_->close(rcb);
}
<commit_msg>Include headers for the XCodec Pipes.<commit_after>#include <common/buffer.h>
#include <common/endian.h>

#include <event/action.h>
#include <event/callback.h>
#include <event/event_system.h>

#include <io/pipe.h>
#include <io/pipe_link.h>
#include <io/pipe_null.h>
#include <io/pipe_pair.h>
#include <io/socket.h>
#include <io/splice.h>
#include <io/splice_pair.h>

#include <net/tcp_client.h>

#include <xcodec/xcodec.h>
#include <xcodec/xcodec_decoder.h>
#include <xcodec/xcodec_encoder.h>
#include <xcodec/xcodec_pipe_pair.h>

#include "proxy_client.h"

ProxyClient::ProxyClient(XCodec *local_codec, XCodec *remote_codec,
			 Socket *local_socket, SocketAddressFamily family,
			 const std::string& remote_name)
: log_("/wanproxy/proxy/client"),
  stop_action_(NULL),
  local_action_(NULL),
  local_codec_(local_codec),
  local_socket_(local_socket),
  remote_action_(NULL),
  remote_codec_(remote_codec),
  remote_socket_(NULL),
  pipes_(),
  incoming_splice_(NULL),
  outgoing_splice_(NULL),
  splice_pair_(NULL),
  splice_action_(NULL)
{
	EventCallback *cb = callback(this, &ProxyClient::connect_complete);
	remote_action_ = TCPClient::connect(&remote_socket_, family, remote_name, cb);
	if (remote_action_ == NULL) {
		ASSERT(remote_socket_ == NULL);

		ERROR(log_) << "Could not connect to: " << remote_name;

		EventCallback *lcb = callback(this, &ProxyClient::close_complete,
					      (void *)local_socket_);
		local_action_ = local_socket_->close(lcb);
		return;
	}

	Callback *scb = callback(this, &ProxyClient::stop);
	stop_action_ = EventSystem::instance()->register_interest(EventInterestStop, scb);
}

ProxyClient::~ProxyClient()
{
	ASSERT(stop_action_ == NULL);
	ASSERT(local_action_ == NULL);
	ASSERT(local_socket_ == NULL);
	ASSERT(remote_action_ == NULL);
	ASSERT(remote_socket_ == NULL);
	ASSERT(incoming_splice_ == NULL);
	ASSERT(outgoing_splice_ == NULL);
	ASSERT(splice_pair_ == NULL);
	ASSERT(splice_action_ == NULL);

	std::set<Pipe *>::iterator it;
	while ((it = pipes_.begin()) != pipes_.end()) {
		Pipe *pipe = *it;
		pipes_.erase(it);

		delete pipe;
	}
}

void
ProxyClient::close_complete(Event e, void *channel)
{
	if (channel == (void *)local_socket_) {
		local_action_->cancel();
		local_action_ = NULL;
	}

	if (channel == (void *)remote_socket_) {
		remote_action_->cancel();
		remote_action_ = NULL;
	}

	switch (e.type_) {
	case Event::Done:
		break;
	default:
		/* XXX Never sure what to do here.  */
		ERROR(log_) << "Unexpected event: " << e;
		return;
	}

	if (channel == (void *)local_socket_) {
		ASSERT(local_socket_ != NULL);
		delete local_socket_;
		local_socket_ = NULL;
	}

	if (channel == (void *)remote_socket_) {
		ASSERT(remote_socket_ != NULL);
		delete remote_socket_;
		remote_socket_ = NULL;
	}

	if (local_socket_ == NULL && remote_socket_ == NULL) {
		delete this;
	}
}

void
ProxyClient::connect_complete(Event e)
{
	remote_action_->cancel();
	remote_action_ = NULL;

	switch (e.type_) {
	case Event::Done:
		break;
	case Event::Error:
		INFO(log_) << "Connect failed: " << e;
		schedule_close();
		return;
	default:
		ERROR(log_) << "Unexpected event: " << e;
		schedule_close();
		return;
	}

	Pipe *incoming_left = new PipeNull();
	pipes_.insert(incoming_left);

	Pipe *incoming_right = new PipeNull();
	pipes_.insert(incoming_right);

	Pipe *incoming_link = new PipeLink(incoming_left, incoming_right);
	pipes_.insert(incoming_link);

	incoming_splice_ = new Splice(remote_socket_, incoming_link, local_socket_);

	Pipe *outgoing_left = new PipeNull();
	pipes_.insert(outgoing_left);

	Pipe *outgoing_right = new PipeNull();
	pipes_.insert(outgoing_right);

	Pipe *outgoing_link = new PipeLink(outgoing_left, outgoing_right);
	pipes_.insert(outgoing_link);

	outgoing_splice_ = new Splice(local_socket_, outgoing_link, remote_socket_);

	splice_pair_ = new SplicePair(outgoing_splice_, incoming_splice_);

	EventCallback *cb = callback(this, &ProxyClient::splice_complete);
	splice_action_ = splice_pair_->start(cb);
}

void
ProxyClient::splice_complete(Event e)
{
	splice_action_->cancel();
	splice_action_ = NULL;

	delete splice_pair_;
	splice_pair_ = NULL;

	delete outgoing_splice_;
	outgoing_splice_ = NULL;

	delete incoming_splice_;
	incoming_splice_ = NULL;

	switch (e.type_) {
	case Event::Done:
		break;
	default:
		ERROR(log_) << "Unexpected event: " << e;
		break;
	}

	schedule_close();
}

void
ProxyClient::stop(void)
{
	stop_action_->cancel();
	stop_action_ = NULL;

	/*
	 * Connecting.
	 */
	if (local_action_ == NULL && remote_action_ != NULL &&
	    splice_action_ == NULL) {
		remote_action_->cancel();
		remote_action_ = NULL;

		schedule_close();
		return;
	}

	/*
	 * Already closing.  Should not happen.
	 */
	if (local_action_ != NULL || remote_action_ != NULL) {
		HALT(log_) << "Client already closing during stop.";
		return;
	}

	schedule_close();
}

void
ProxyClient::schedule_close(void)
{
	if (stop_action_ != NULL) {
		stop_action_->cancel();
		stop_action_ = NULL;
	}

	if (splice_pair_ != NULL) {
		if (splice_action_ != NULL) {
			splice_action_->cancel();
			splice_action_ = NULL;
		}

		ASSERT(outgoing_splice_ != NULL);
		ASSERT(incoming_splice_ != NULL);

		delete splice_pair_;
		splice_pair_ = NULL;

		delete outgoing_splice_;
		outgoing_splice_ = NULL;

		delete incoming_splice_;
		incoming_splice_ = NULL;
	}

	ASSERT(local_action_ == NULL);
	ASSERT(local_socket_ != NULL);
	EventCallback *lcb = callback(this, &ProxyClient::close_complete,
				      (void *)local_socket_);
	local_action_ = local_socket_->close(lcb);

	ASSERT(remote_action_ == NULL);
	ASSERT(remote_socket_ != NULL);
	EventCallback *rcb = callback(this, &ProxyClient::close_complete,
				      (void *)remote_socket_);
	remote_action_ = remote_socket_->close(rcb);
}
<|endoftext|>


<commit_before>// maketable.cpp
//
#include "dataserver/maketable/maketable.h"

#if SDL_DEBUG
namespace sdl { namespace db { namespace make { namespace sample {
struct dbo_META {
    struct col {
        struct Id : meta::col<0, 0, scalartype::t_int, 4, meta::key<true, 0, sortorder::ASC>> { static constexpr const char * name() { return "Id"; } };
        struct Id2 : meta::col<1, 4, scalartype::t_bigint, 8, meta::key<true, 1, sortorder::DESC>> { static constexpr const char * name() { return "Id2"; } };
        struct Col1 : meta::col<2, 12, scalartype::t_char, 255> { static constexpr const char * name() { return "Col1"; } };
    };
    typedef TL::Seq<
        col::Id
        ,col::Id2
        ,col::Col1
    >::Type type_list;
    struct clustered_META {
        using T0 = meta::index_col<col::Id>;
        using T1 = meta::index_col<col::Id2, T0::offset + sizeof(T0::type)>;
        typedef TL::Seq<T0, T1>::Type type_list;
    };
    struct clustered final : make_clustered<clustered_META> {
#pragma pack(push, 1)
        struct key_type {
            T0::type _0;
            T1::type _1;
            void get(Int2Type<0>) const && = delete;
            void get(Int2Type<1>) const && = delete;
            void set(Int2Type<0>) && = delete;
            void set(Int2Type<1>) && = delete;
            T0::type const & get(Int2Type<0>) const & { return _0; }
            T1::type const & get(Int2Type<1>) const & { return _1; }
            T0::type & set(Int2Type<0>) & { return _0; }
            T1::type & set(Int2Type<1>) & { return _1; }
            template<size_t i> void get() && = delete;
            template<size_t i> void set() && = delete;
            template<size_t i> auto get() & -> decltype(get(Int2Type<i>())) { return get(Int2Type<i>()); }
            template<size_t i> auto set() & -> decltype(set(Int2Type<i>())) { return set(Int2Type<i>()); }
            using this_clustered = clustered;
        };
#pragma pack(pop)
        static const char * name() { return ""; }
        static bool is_less(key_type const & x, key_type const & y) {
            if (meta::is_less<T0>::less(x._0, y._0)) return true;
            if (meta::is_less<T0>::less(y._0, x._0)) return false;
            if (meta::is_less<T1>::less(x._1, y._1)) return true;
            return false; // keys are equal
        }
        static bool less_first(decltype(key_type()._0) const & x, decltype(key_type()._0) const & y) {
            if (meta::is_less<T0>::less(x, y)) return true;
            return false;
        }
        static constexpr pageType::type root_page_type = pageType::type::index;
    };
    static constexpr const char * name() { return ""; }
    static constexpr int32 id = 0;
};

class dbo_table final : public dbo_META, public make_base_table<dbo_META> {
    using base_table = make_base_table<dbo_META>;
    using this_table = dbo_table;
public:
    class record final : public base_record<this_table> {
        using base = base_record<this_table>;
        using access = base_access<this_table, record>;
        using query = make_query<this_table, record>;
        friend access;
        friend query;
        friend this_table;
        record(this_table const * p, row_head const * h) noexcept : base(p, h) {}
    public:
        record() = default;
        auto Id() const -> col::Id::ret_type { return val<col::Id>(); }
        auto Col1() const -> col::Col1::ret_type { return val<col::Col1>(); }
    };
    static constexpr size_t static_record_count = 0;
public:
    using iterator = record::access::iterator;
    using query_type = record::query;
    explicit dbo_table(database const * p, shared_usertable const & s)
        : base_table(p, s), _record(this), query(this, p)
    {}
    iterator begin() const { return _record.begin(); }
    iterator end() const { return _record.end(); }
    query_type * operator ->() { return &query; }
private:
    const record::access _record;
public:
    query_type query;
};

template <class type_list> struct test_processor;
template <> struct test_processor<NullType> {
    static void test(){}
};
template <class T, class U> // T = meta::col_type
struct test_processor< Typelist<T, U> > {
    static void test(){
        T::test();
        test_processor<U>::test();
    }
};
void test_sample_table(sample::dbo_table * const table) {
    using T = sample::dbo_table;
    static_assert(T::col_size == 3, "");
    static_assert(T::col_fixed, "");
    static_assert(sizeof(T::record) == sizeof(void *), "");
    using clustered = T::clustered;
    using query_type = T::query_type;
    using key_type = clustered::key_type;
    static_assert(clustered::index_size == 2, "");
    static_assert(sizeof(key_type) ==
        sizeof(clustered::T0::type) +
        sizeof(clustered::T1::type),
        "");
    static_assert(clustered::T0::offset == 0, "");
    static_assert(clustered::T1::offset == 4, "");
    A_STATIC_ASSERT_IS_POD(key_type);
    if (table) {
        T & tab = *table;
        for (auto p : tab) {
            if (p.Id()) {}
        }
        tab->scan_if([](T::record){
            return true;
        });
        tab.query.scan_if([](T::record){
            return true;
        });
        if (auto found = tab->find([](T::record p){
            return p.Id() > 0;
        })) {
            SDL_ASSERT(found.Id() > 0);
        }
        std::vector<T::record> range;
        tab->scan_if([&range](T::record p){
            if (p.Id() > 0) {
                range.push_back(p);
                return true;
            }
            return false;
        });
        key_type test{};
        auto _0 = test.get<0>();
        auto _1 = test.get<1>();
        static_assert(std::is_same<int32 const &, decltype(test.get<0>())>::value, "");
        static_assert(std::is_same<int64 const &, decltype(test.get<1>())>::value, "");
        test.set<0>() = _0;
        test.set<1>() = _1;
        static_assert(std::is_same<int32 &, decltype(test.set<0>())>::value, "");
        static_assert(std::is_same<int64 &, decltype(test.set<1>())>::value, "");
        const auto key = tab->read_key(tab->find([](T::record){ return true; }));
        if (auto p = tab->find_with_index(key)) {
            A_STATIC_CHECK_TYPE(T::record, p);
        }
        if (1) {
            using namespace where_;
            tab->SELECT | WHERE<T::col::Id>{1} | LESS<T::col::Id2>{1} | GREATER<T::col::Id2>{2};
            tab->SELECT
                | IN<T::col::Id>{1,2,3}
                && ORDER_BY<T::col::Id>{}
                && NOT<T::col::Id2>{1}
                && ORDER_BY<T::col::Col1>{}
                ;
            //FIXME: failed build on Ubuntu ?
            //auto r1 = (tab->SELECT | BETWEEN<T::col::Id>{1,2} && ORDER_BY<T::col::Id>{}).VALUES();
        }
    }
    if (1) {
        using S = query_type;
        const key_type x = S::make_key(1, 2);
        const key_type y = S::make_key(2, 1);
        SDL_ASSERT(x._0 == 1);
        SDL_ASSERT(x._1 == 2);
        SDL_ASSERT(x < y);
        SDL_ASSERT(x != y);
        SDL_ASSERT(x == x);
        auto in = { S::make_key(0, 1), S::make_key(1, 1) };
        for (auto const & k : in) {
            A_STATIC_CHECK_TYPE(key_type const &, k);
            SDL_ASSERT(!meta::key_to_string<clustered::type_list>::to_str(k).empty());
        }
    }
}
class unit_test {
public:
    unit_test() {
        struct col {
            using t_int                 = meta::col<0, 0, scalartype::t_int, 4, meta::key_true>;
            using t_bigint              = meta::col<0, 0, scalartype::t_bigint, 8>;
            using t_smallint            = meta::col<0, 0, scalartype::t_smallint, 2>;
            using t_float               = meta::col<0, 0, scalartype::t_float, 8>;
            using t_real                = meta::col<0, 0, scalartype::t_real, 4>;
            using t_smalldatetime       = meta::col<0, 0, scalartype::t_smalldatetime, 4>;
            using t_uniqueidentifier    = meta::col<0, 0, scalartype::t_uniqueidentifier, 16>;
            using t_char                = meta::col<0, 0, scalartype::t_char, 255>;
            using t_nchar               = meta::col<0, 0, scalartype::t_nchar, 20>;
            using t_varchar             = meta::col<0, 0, scalartype::t_varchar, 255>;
            using t_geography            = meta::col<0, 0, scalartype::t_geography, -1>;
        };
        typedef TL::Seq<
            col::t_int
            ,col::t_bigint
            ,col::t_smallint
            ,col::t_float
            ,col::t_real
            ,col::t_smalldatetime
            ,col::t_uniqueidentifier
            ,col::t_char
            ,col::t_nchar
            ,col::t_varchar
            ,col::t_geography
        >::Type type_list;
        test_processor<type_list>::test();
        test_sample_table(nullptr);
        if (0) {
            SDL_TRACE(typeid(sample::dbo_META::col::Id).name());
            SDL_TRACE(typeid(sample::dbo_META::col::Col1).name());
        }
        if (0) {
            make::index_tree<dbo_META::clustered::key_type> test(nullptr, nullptr);
        }
        if (0) {
            using namespace where_;
            using T1 = operator_list<operator_::OR>;
            using T2 = oper_::append<T1, operator_::AND>::Result;
            using T3 = oper_::reverse<T2>::Result;
            static_assert(T3::Head == operator_::AND, "");
            static_assert(T3::Tail::Head == operator_::OR, "");
        }
    }
};
static unit_test s_test;
} // sample
} // make
} // db
} // sdl
#endif //#if SV_DEBUG

/*tab.scan([](auto row) {});
tab.select({ if row return true; });
tab.find_pk(int[]);
tab.find_less(int, use_indexes);
tab.find_less(300).select( {bool} );
tab.find_less(300).top(10); // last(10)
tab.top(5).union( tab.last(5) );
tab.find( {bool} )
*/
<commit_msg>decltype(auto) return type<commit_after>// maketable.cpp
//
#include "dataserver/maketable/maketable.h"

#if SDL_DEBUG
namespace sdl { namespace db { namespace make { namespace sample {
struct dbo_META {
    struct col {
        struct Id : meta::col<0, 0, scalartype::t_int, 4, meta::key<true, 0, sortorder::ASC>> { static constexpr const char * name() { return "Id"; } };
        struct Id2 : meta::col<1, 4, scalartype::t_bigint, 8, meta::key<true, 1, sortorder::DESC>> { static constexpr const char * name() { return "Id2"; } };
        struct Col1 : meta::col<2, 12, scalartype::t_char, 255> { static constexpr const char * name() { return "Col1"; } };
    };
    typedef TL::Seq<
        col::Id
        ,col::Id2
        ,col::Col1
    >::Type type_list;
    struct clustered_META {
        using T0 = meta::index_col<col::Id>;
        using T1 = meta::index_col<col::Id2, T0::offset + sizeof(T0::type)>;
        typedef TL::Seq<T0, T1>::Type type_list;
    };
    struct clustered final : make_clustered<clustered_META> {
#pragma pack(push, 1)
        struct key_type {
            T0::type _0;
            T1::type _1;
            void get(Int2Type<0>) const && = delete;
            void get(Int2Type<1>) const && = delete;
            void set(Int2Type<0>) && = delete;
            void set(Int2Type<1>) && = delete;
            T0::type const & get(Int2Type<0>) const & { return _0; }
            T1::type const & get(Int2Type<1>) const & { return _1; }
            T0::type & set(Int2Type<0>) & { return _0; }
            T1::type & set(Int2Type<1>) & { return _1; }
            template<size_t i> void get() && = delete;
            template<size_t i> void set() && = delete;
            template<size_t i> decltype(auto) get() & { return get(Int2Type<i>()); }
            template<size_t i> decltype(auto) set() & { return set(Int2Type<i>()); }
            using this_clustered = clustered;
        };
#pragma pack(pop)
        static const char * name() { return ""; }
        static bool is_less(key_type const & x, key_type const & y) {
            if (meta::is_less<T0>::less(x._0, y._0)) return true;
            if (meta::is_less<T0>::less(y._0, x._0)) return false;
            if (meta::is_less<T1>::less(x._1, y._1)) return true;
            return false; // keys are equal
        }
        static bool less_first(decltype(key_type()._0) const & x, decltype(key_type()._0) const & y) {
            if (meta::is_less<T0>::less(x, y)) return true;
            return false;
        }
        static constexpr pageType::type root_page_type = pageType::type::index;
    };
    static constexpr const char * name() { return ""; }
    static constexpr int32 id = 0;
};

class dbo_table final : public dbo_META, public make_base_table<dbo_META> {
    using base_table = make_base_table<dbo_META>;
    using this_table = dbo_table;
public:
    class record final : public base_record<this_table> {
        using base = base_record<this_table>;
        using access = base_access<this_table, record>;
        using query = make_query<this_table, record>;
        friend access;
        friend query;
        friend this_table;
        record(this_table const * p, row_head const * h) noexcept : base(p, h) {}
    public:
        record() = default;
        decltype(auto) Id() const { return val<col::Id>(); }
        decltype(auto) Col1() const { return val<col::Col1>(); }
    };
    static constexpr size_t static_record_count = 0;
public:
    using iterator = record::access::iterator;
    using query_type = record::query;
    explicit dbo_table(database const * p, shared_usertable const & s)
        : base_table(p, s), _record(this), query(this, p)
    {}
    iterator begin() const { return _record.begin(); }
    iterator end() const { return _record.end(); }
    query_type * operator ->() { return &query; }
private:
    const record::access _record;
public:
    query_type query;
};

template <class type_list> struct test_processor;
template <> struct test_processor<NullType> {
    static void test(){}
};
template <class T, class U> // T = meta::col_type
struct test_processor< Typelist<T, U> > {
    static void test(){
        T::test();
        test_processor<U>::test();
    }
};
void test_sample_table(sample::dbo_table * const table) {
    using T = sample::dbo_table;
    static_assert(T::col_size == 3, "");
    static_assert(T::col_fixed, "");
    static_assert(sizeof(T::record) == sizeof(void *), "");
    using clustered = T::clustered;
    using query_type = T::query_type;
    using key_type = clustered::key_type;
    static_assert(clustered::index_size == 2, "");
    static_assert(sizeof(key_type) ==
        sizeof(clustered::T0::type) +
        sizeof(clustered::T1::type),
        "");
    static_assert(clustered::T0::offset == 0, "");
    static_assert(clustered::T1::offset == 4, "");
    A_STATIC_ASSERT_IS_POD(key_type);
    if (table) {
        T & tab = *table;
        for (auto p : tab) {
            if (p.Id()) {}
        }
        tab->scan_if([](T::record){
            return true;
        });
        tab.query.scan_if([](T::record){
            return true;
        });
        if (auto found = tab->find([](T::record p){
            return p.Id() > 0;
        })) {
            SDL_ASSERT(found.Id() > 0);
        }
        std::vector<T::record> range;
        tab->scan_if([&range](T::record p){
            if (p.Id() > 0) {
                range.push_back(p);
                return true;
            }
            return false;
        });
        key_type test{};
        auto _0 = test.get<0>();
        auto _1 = test.get<1>();
        static_assert(std::is_same<int32 const &, decltype(test.get<0>())>::value, "");
        static_assert(std::is_same<int64 const &, decltype(test.get<1>())>::value, "");
        test.set<0>() = _0;
        test.set<1>() = _1;
        static_assert(std::is_same<int32 &, decltype(test.set<0>())>::value, "");
        static_assert(std::is_same<int64 &, decltype(test.set<1>())>::value, "");
        const auto key = tab->read_key(tab->find([](T::record){ return true; }));
        if (auto p = tab->find_with_index(key)) {
            A_STATIC_CHECK_TYPE(T::record, p);
        }
        if (1) {
            using namespace where_;
            tab->SELECT | WHERE<T::col::Id>{1} | LESS<T::col::Id2>{1} | GREATER<T::col::Id2>{2};
            tab->SELECT
                | IN<T::col::Id>{1,2,3}
                && ORDER_BY<T::col::Id>{}
                && NOT<T::col::Id2>{1}
                && ORDER_BY<T::col::Col1>{}
                ;
            //FIXME: failed build on Ubuntu ?
            //auto r1 = (tab->SELECT | BETWEEN<T::col::Id>{1,2} && ORDER_BY<T::col::Id>{}).VALUES();
        }
    }
    if (1) {
        using S = query_type;
        const key_type x = S::make_key(1, 2);
        const key_type y = S::make_key(2, 1);
        SDL_ASSERT(x._0 == 1);
        SDL_ASSERT(x._1 == 2);
        SDL_ASSERT(x < y);
        SDL_ASSERT(x != y);
        SDL_ASSERT(x == x);
        auto in = { S::make_key(0, 1), S::make_key(1, 1) };
        for (auto const & k : in) {
            A_STATIC_CHECK_TYPE(key_type const &, k);
            SDL_ASSERT(!meta::key_to_string<clustered::type_list>::to_str(k).empty());
        }
    }
}
class unit_test {
public:
    unit_test() {
        struct col {
            using t_int                 = meta::col<0, 0, scalartype::t_int, 4, meta::key_true>;
            using t_bigint              = meta::col<0, 0, scalartype::t_bigint, 8>;
            using t_smallint            = meta::col<0, 0, scalartype::t_smallint, 2>;
            using t_float               = meta::col<0, 0, scalartype::t_float, 8>;
            using t_real                = meta::col<0, 0, scalartype::t_real, 4>;
            using t_smalldatetime       = meta::col<0, 0, scalartype::t_smalldatetime, 4>;
            using t_uniqueidentifier    = meta::col<0, 0, scalartype::t_uniqueidentifier, 16>;
            using t_char                = meta::col<0, 0, scalartype::t_char, 255>;
            using t_nchar               = meta::col<0, 0, scalartype::t_nchar, 20>;
            using t_varchar             = meta::col<0, 0, scalartype::t_varchar, 255>;
            using t_geography            = meta::col<0, 0, scalartype::t_geography, -1>;
        };
        typedef TL::Seq<
            col::t_int
            ,col::t_bigint
            ,col::t_smallint
            ,col::t_float
            ,col::t_real
            ,col::t_smalldatetime
            ,col::t_uniqueidentifier
            ,col::t_char
            ,col::t_nchar
            ,col::t_varchar
            ,col::t_geography
        >::Type type_list;
        test_processor<type_list>::test();
        test_sample_table(nullptr);
        if (0) {
            SDL_TRACE(typeid(sample::dbo_META::col::Id).name());
            SDL_TRACE(typeid(sample::dbo_META::col::Col1).name());
        }
        if (0) {
            make::index_tree<dbo_META::clustered::key_type> test(nullptr, nullptr);
        }
        if (0) {
            using namespace where_;
            using T1 = operator_list<operator_::OR>;
            using T2 = oper_::append<T1, operator_::AND>::Result;
            using T3 = oper_::reverse<T2>::Result;
            static_assert(T3::Head == operator_::AND, "");
            static_assert(T3::Tail::Head == operator_::OR, "");
        }
    }
};
static unit_test s_test;
} // sample
} // make
} // db
} // sdl
#endif //#if SV_DEBUG

/*tab.scan([](auto row) {});
tab.select({ if row return true; });
tab.find_pk(int[]);
tab.find_less(int, use_indexes);
tab.find_less(300).select( {bool} );
tab.find_less(300).top(10); // last(10)
tab.top(5).union( tab.last(5) );
tab.find( {bool} )
*/
<|endoftext|>


<commit_before>#include <memory>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

#include "base_test.hpp"
#include "gtest/gtest.h"

#include "storage/base_segment.hpp"
#include "storage/chunk.hpp"
#include "storage/index/b_tree/b_tree_index.hpp"
#include "types.hpp"

namespace opossum {

class BTreeIndexTest : public BaseTest {
 protected:
  void SetUp() override {
    values = {"hotel", "delta", "frank", "delta", "apple", "charlie", "charlie", "inbox"};
    segment = std::make_shared<ValueSegment<std::string>>(values);
    sorted = {"apple", "charlie", "charlie", "delta", "delta", "frank", "hotel", "inbox"};
    index = std::make_shared<BTreeIndex>(std::vector<std::shared_ptr<const BaseSegment>>({segment}));

    chunk_offsets = &(index->_impl->_chunk_offsets);
  }

  std::vector<std::string> values;
  std::vector<std::string> sorted;
  std::shared_ptr<BTreeIndex> index = nullptr;
  std::shared_ptr<ValueSegment<std::string>> segment = nullptr;

  /**
   * Use pointers to inner data structures of BTreeIndex in order to bypass the
   * private scope. Since the variable is set in setup() references are not possible.
   */
  std::vector<ChunkOffset>* chunk_offsets;
};

TEST_F(BTreeIndexTest, ChunkOffsets) {
  for (size_t i = 0; i < values.size(); i++) {
    EXPECT_EQ(values[chunk_offsets->at(i)], sorted[i]);
  }
}

TEST_F(BTreeIndexTest, IndexProbes) {
  auto begin = index->cbegin();
  EXPECT_EQ(index->lower_bound({"apple"}) - begin, 0);
  EXPECT_EQ(index->upper_bound({"apple"}) - begin, 1);

  EXPECT_EQ(index->lower_bound({"charlie"}) - begin, 1);
  EXPECT_EQ(index->upper_bound({"charlie"}) - begin, 3);

  EXPECT_EQ(index->lower_bound({"delta"}) - begin, 3);
  EXPECT_EQ(index->upper_bound({"delta"}) - begin, 5);

  EXPECT_EQ(index->lower_bound({"frank"}) - begin, 5);
  EXPECT_EQ(index->upper_bound({"frank"}) - begin, 6);

  EXPECT_EQ(index->lower_bound({"hotel"}) - begin, 6);
  EXPECT_EQ(index->upper_bound({"hotel"}) - begin, 7);

  EXPECT_EQ(index->lower_bound({"inbox"}) - begin, 7);
  EXPECT_EQ(index->upper_bound({"inbox"}) - begin, 8);
}

// The following tests contain switches for different implementations of the stdlib.
// Short String Optimization (SSO) stores strings of a certain size in the std::string object itself.
// Only strings exceeding this size (15 for libstdc++ and 22 for libc++) are stored on the heap.

TEST_F(BTreeIndexTest, MemoryConsumptionVeryShortString) {
  values = {"h", "d", "f", "d", "a", "c", "c", "i", "b", "z", "x"};
  segment = std::make_shared<ValueSegment<std::string>>(values);
  index = std::make_shared<BTreeIndex>(std::vector<std::shared_ptr<const BaseSegment>>({segment}));

// Index memory consumption depends on implementation of std::string.
#ifdef __GLIBCXX__
  // libstdc++:
  //   848 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  44 number of elements (11) * sizeof(ChunkOffset) (4)
  // =  916
  EXPECT_EQ(index->memory_consumption(), 916u);
#else
  // libc++:
  //   808 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  44 number of elements (11) * sizeof(ChunkOffset) (4)
  // =  876
  EXPECT_EQ(index->memory_consumption(), 876u);
#endif
}

TEST_F(BTreeIndexTest, MemoryConsumptionShortString) {
  ASSERT_GE(std::string("").capacity(), 7u)
      << "Short String Optimization (SSO) is expected to hold at least 7 characters";

// Index memory consumption depends on implementation of std::string.
#ifdef __GLIBCXX__
  // libstdc++:
  //   264 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  32 number of elements (8) * sizeof(ChunkOffset) (4)
  // =  320
  EXPECT_EQ(index->memory_consumption(), 320u);
#else
  // libc++:
  //   248 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  32 number of elements (8) * sizeof(ChunkOffset) (4)
  // =  304
  EXPECT_EQ(index->memory_consumption(), 304u);
#endif
}

TEST_F(BTreeIndexTest, MemoryConsumptionLongString) {
  ASSERT_LE(std::string("").capacity(), 22u)
      << "Short String Optimization (SSO) is expected to hold at maximum 20 characters";

  values = {"hotelhotelhotelhotelhotel", "deltadeltadeltadelta",  "frankfrankfrankfrank",  "deltadeltadeltadelta",
            "appleappleappleapple",      "charliecharliecharlie", "charliecharliecharlie", "inboxinboxinboxinbox"};
  segment = std::make_shared<ValueSegment<std::string>>(values);
  index = std::make_shared<BTreeIndex>(std::vector<std::shared_ptr<const BaseSegment>>({segment}));

// Index memory consumption depends on implementation of std::string.
#ifdef __GLIBCXX__
  // libstdc++:
  //   264 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  32 number of elements (8) * sizeof(ChunkOffset) (4)
  // +  20 "appleappleappleapple"
  // +  21 "charliecharliecharlie"
  // +  20 "deltadeltadeltadelta"
  // +  20 "frankfrankfrankfrank"
  // +  20 "inboxinboxinboxinbox"
  // +  25 "hotelhotelhotelhotelhotel"
  // =  446
  EXPECT_EQ(index->memory_consumption(), 446u);
#else
  // libc++ Only one string exceeds the reserved space (22 characters) for small strings:
  //   248 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  32 number of elements (8) * sizeof(ChunkOffset) (4)
  // +  25 "hotelhotelhotelhotelhotel"
  // =  329
  EXPECT_EQ(index->memory_consumption(), 329u);
#endif
}

}  // namespace opossum
<commit_msg>Fix assertion description in btree_index_text.cpp (#1273)<commit_after>#include <memory>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

#include "base_test.hpp"
#include "gtest/gtest.h"

#include "storage/base_segment.hpp"
#include "storage/chunk.hpp"
#include "storage/index/b_tree/b_tree_index.hpp"
#include "types.hpp"

namespace opossum {

class BTreeIndexTest : public BaseTest {
 protected:
  void SetUp() override {
    values = {"hotel", "delta", "frank", "delta", "apple", "charlie", "charlie", "inbox"};
    segment = std::make_shared<ValueSegment<std::string>>(values);
    sorted = {"apple", "charlie", "charlie", "delta", "delta", "frank", "hotel", "inbox"};
    index = std::make_shared<BTreeIndex>(std::vector<std::shared_ptr<const BaseSegment>>({segment}));

    chunk_offsets = &(index->_impl->_chunk_offsets);
  }

  std::vector<std::string> values;
  std::vector<std::string> sorted;
  std::shared_ptr<BTreeIndex> index = nullptr;
  std::shared_ptr<ValueSegment<std::string>> segment = nullptr;

  /**
   * Use pointers to inner data structures of BTreeIndex in order to bypass the
   * private scope. Since the variable is set in setup() references are not possible.
   */
  std::vector<ChunkOffset>* chunk_offsets;
};

TEST_F(BTreeIndexTest, ChunkOffsets) {
  for (size_t i = 0; i < values.size(); i++) {
    EXPECT_EQ(values[chunk_offsets->at(i)], sorted[i]);
  }
}

TEST_F(BTreeIndexTest, IndexProbes) {
  auto begin = index->cbegin();
  EXPECT_EQ(index->lower_bound({"apple"}) - begin, 0);
  EXPECT_EQ(index->upper_bound({"apple"}) - begin, 1);

  EXPECT_EQ(index->lower_bound({"charlie"}) - begin, 1);
  EXPECT_EQ(index->upper_bound({"charlie"}) - begin, 3);

  EXPECT_EQ(index->lower_bound({"delta"}) - begin, 3);
  EXPECT_EQ(index->upper_bound({"delta"}) - begin, 5);

  EXPECT_EQ(index->lower_bound({"frank"}) - begin, 5);
  EXPECT_EQ(index->upper_bound({"frank"}) - begin, 6);

  EXPECT_EQ(index->lower_bound({"hotel"}) - begin, 6);
  EXPECT_EQ(index->upper_bound({"hotel"}) - begin, 7);

  EXPECT_EQ(index->lower_bound({"inbox"}) - begin, 7);
  EXPECT_EQ(index->upper_bound({"inbox"}) - begin, 8);
}

// The following tests contain switches for different implementations of the stdlib.
// Short String Optimization (SSO) stores strings of a certain size in the std::string object itself.
// Only strings exceeding this size (15 for libstdc++ and 22 for libc++) are stored on the heap.

TEST_F(BTreeIndexTest, MemoryConsumptionVeryShortString) {
  values = {"h", "d", "f", "d", "a", "c", "c", "i", "b", "z", "x"};
  segment = std::make_shared<ValueSegment<std::string>>(values);
  index = std::make_shared<BTreeIndex>(std::vector<std::shared_ptr<const BaseSegment>>({segment}));

// Index memory consumption depends on implementation of std::string.
#ifdef __GLIBCXX__
  // libstdc++:
  //   848 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  44 number of elements (11) * sizeof(ChunkOffset) (4)
  // =  916
  EXPECT_EQ(index->memory_consumption(), 916u);
#else
  // libc++:
  //   808 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  44 number of elements (11) * sizeof(ChunkOffset) (4)
  // =  876
  EXPECT_EQ(index->memory_consumption(), 876u);
#endif
}

TEST_F(BTreeIndexTest, MemoryConsumptionShortString) {
  ASSERT_GE(std::string("").capacity(), 7u)
      << "Short String Optimization (SSO) is expected to hold at least 7 characters";

// Index memory consumption depends on implementation of std::string.
#ifdef __GLIBCXX__
  // libstdc++:
  //   264 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  32 number of elements (8) * sizeof(ChunkOffset) (4)
  // =  320
  EXPECT_EQ(index->memory_consumption(), 320u);
#else
  // libc++:
  //   248 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  32 number of elements (8) * sizeof(ChunkOffset) (4)
  // =  304
  EXPECT_EQ(index->memory_consumption(), 304u);
#endif
}

TEST_F(BTreeIndexTest, MemoryConsumptionLongString) {
  ASSERT_LE(std::string("").capacity(), 22u)
      << "Short String Optimization (SSO) is expected to hold at maximum 22 characters";

  values = {"hotelhotelhotelhotelhotel", "deltadeltadeltadelta",  "frankfrankfrankfrank",  "deltadeltadeltadelta",
            "appleappleappleapple",      "charliecharliecharlie", "charliecharliecharlie", "inboxinboxinboxinbox"};
  segment = std::make_shared<ValueSegment<std::string>>(values);
  index = std::make_shared<BTreeIndex>(std::vector<std::shared_ptr<const BaseSegment>>({segment}));

// Index memory consumption depends on implementation of std::string.
#ifdef __GLIBCXX__
  // libstdc++:
  //   264 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  32 number of elements (8) * sizeof(ChunkOffset) (4)
  // +  20 "appleappleappleapple"
  // +  21 "charliecharliecharlie"
  // +  20 "deltadeltadeltadelta"
  // +  20 "frankfrankfrankfrank"
  // +  20 "inboxinboxinboxinbox"
  // +  25 "hotelhotelhotelhotelhotel"
  // =  446
  EXPECT_EQ(index->memory_consumption(), 446u);
#else
  // libc++ Only one string exceeds the reserved space (22 characters) for small strings:
  //   248 (reported by cpp_btree implementation)
  // +  24 std::vector<ChunkOffset> object overhead
  // +  32 number of elements (8) * sizeof(ChunkOffset) (4)
  // +  25 "hotelhotelhotelhotelhotel"
  // =  329
  EXPECT_EQ(index->memory_consumption(), 329u);
#endif
}

}  // namespace opossum
<|endoftext|>


<commit_before>/*
 *   Copyright 2012 Coherent Theory LLC
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2, 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 General Public License for more details
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "ratingattributesjobmodel.h"

#include "ratingattributesjob.h"
#include "session.h"
#include "networkjob.h"

#include <QtCore/QVariant>
#include <QtCore/QDebug>
#include <QtCore/QMetaEnum>
#include <QDebug>
namespace Bodega
{

class RatingAttributesJobModel::Private {
public:
    Private(RatingAttributesJobModel *parent);

    RatingAttributesJobModel *q;
    Session *session;
    void fetchRatingAttributes();
    void ratingAttributesJobFinished(Bodega::NetworkJob *job);
    void assetJobFinished(Bodega::NetworkJob *job);

    QString findAverageRating(const QString ratingAttributeId) const;
    QString findRatingsCount(const QString ratingAttributeId) const;

    QString assetId;
    QList<RatingAttributes> ratingAttributes;
    AssetInfo assetInfo;
};

RatingAttributesJobModel::Private::Private(RatingAttributesJobModel *parent)
    : q(parent),
      session(0)
{
}

void RatingAttributesJobModel::Private::fetchRatingAttributes()
{
    RatingAttributesJob *job = session->listRatingAttributes(assetId);

    q->beginResetModel();
    ratingAttributes.clear();
    assetInfo.clear();
    q->endResetModel();

    connect(job, SIGNAL(jobFinished(Bodega::NetworkJob *)),
            q, SLOT(ratingAttributesJobFinished(Bodega::NetworkJob *)));
}

void RatingAttributesJobModel::Private::ratingAttributesJobFinished(Bodega::NetworkJob *job)
{
    RatingAttributesJob *ratingAttributesJob = qobject_cast<RatingAttributesJob*>(job);

    if (!ratingAttributesJob) {
        return;
    }

    ratingAttributesJob->deleteLater();

    if (ratingAttributesJob->failed()) {
        return;
    }

    const int begin = 0;
    const int end = qMax(begin, ratingAttributes.count() + begin -1);
    q->beginInsertRows(QModelIndex(), begin, end);
    ratingAttributes = ratingAttributesJob->ratingAttributes();

    AssetJob *assetJob = session->asset(assetId, AssetJob::Ratings);
    connect(assetJob, SIGNAL(jobFinished(Bodega::NetworkJob *)),
            q, SLOT(assetJobFinished(Bodega::NetworkJob *)));
}

void RatingAttributesJobModel::Private::assetJobFinished(Bodega::NetworkJob *job)
{
    AssetJob *assetJob = qobject_cast<AssetJob*>(job);

    if (!assetJob) {
        return;
    }

    assetJob->deleteLater();

    if (assetJob->failed()) {
        q->endInsertRows();
        return;
    }
    assetInfo = assetJob->info();
    q->endInsertRows();
}

QString RatingAttributesJobModel::Private::findAverageRating(const QString ratingAttributeId) const
{
    foreach(const AssetInfo::AssetInfoRatings &info, assetInfo.ratings) {
        if (ratingAttributeId == info.attributeId) {
            return info.averageRating;
        }
    }
    return QString();
}

QString RatingAttributesJobModel::Private::findRatingsCount(const QString ratingAttributeId) const
{
    foreach(const AssetInfo::AssetInfoRatings &info, assetInfo.ratings) {
        if (ratingAttributeId == info.attributeId) {
            return info.ratingsCount;
        }
    }
    return QString();
}

RatingAttributesJobModel::RatingAttributesJobModel(QObject *parent)
    : QAbstractItemModel(parent),
      d(new Private(this))
{
    // set the role names based on the values of the DisplayRoles enum for
    //  the sake of QML
    QHash<int, QByteArray> roles;

    QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("DisplayRoles"));
    for (int i = 0; i < e.keyCount(); ++i) {
        roles.insert(e.value(i), e.key(i));
    }
    setRoleNames(roles);

    connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
            this, SIGNAL(countChanged()));
    connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)),
            this, SIGNAL(countChanged()));
    connect(this, SIGNAL(modelReset()),
            this, SIGNAL(countChanged()));
    connect(this, SIGNAL(assetIdChanged()),
            this, SLOT(fetchRatingAttributes()));
}

RatingAttributesJobModel::~RatingAttributesJobModel()
{
    delete d;
}

QString RatingAttributesJobModel::assetId() const
{
    return d->assetId;
}

void RatingAttributesJobModel::setAssetId(const QString& assetId)
{
    d->assetId = assetId;
    emit assetIdChanged();
}

int RatingAttributesJobModel::columnCount(const QModelIndex &parent) const
{
    return 1;
}

QVariant RatingAttributesJobModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() >= d->ratingAttributes.count()) {
        return QVariant();
    }

    switch (role) {
        case Name: {
            return d->ratingAttributes.at(index.row()).name;
        }
        case LowDesc: {
            return d->ratingAttributes.at(index.row()).lowDesc;
        }
        case HighDesc: {
            return d->ratingAttributes.at(index.row()).highDesc;
        }
        case AssetType: {
            return d->ratingAttributes.at(index.row()).assetType;
        }
        case RatingsCount: {
            return d->findRatingsCount(d->ratingAttributes.at(index.row()).id);
        }
        case AverageRating: {
            return d->findAverageRating(d->ratingAttributes.at(index.row()).id);
        }

        default: {
            return QVariant();
        }
    }
}

Qt::ItemFlags RatingAttributesJobModel::flags(const QModelIndex &index) const
{
    if (index.isValid()) {
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    } else {
        return Qt::NoItemFlags;
    }
}

bool RatingAttributesJobModel::hasChildren(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return false;
}

QVariant RatingAttributesJobModel::headerData(int section, Qt::Orientation orientation,
                           int role) const
{
    return QVariant();
}

QModelIndex RatingAttributesJobModel::index(int row, int column, const QModelIndex &parent) const
{
    if (column > 0) {
        return QModelIndex();
    }

    if (row < 0 || row >= d->ratingAttributes.count()) {
        return QModelIndex();
    }

    return createIndex(row, column);
}

QMap<int, QVariant> RatingAttributesJobModel::itemData(const QModelIndex &index) const
{
    return QMap<int, QVariant>();
}

QModelIndex RatingAttributesJobModel::parent(const QModelIndex &index) const
{
    return QModelIndex();
}

int RatingAttributesJobModel::rowCount(const QModelIndex &parent) const
{
    return d->ratingAttributes.size();
}

void RatingAttributesJobModel::setSession(Session *session)
{
    if (session == d->session) {
        return;
    }

    if (d->session) {
        //not connected directly, so disconnect everything
        d->session->disconnect(this);
    }
    d->session = session;

    if (!d->session) {
        return;
    }
}

Session *RatingAttributesJobModel::session() const
{
    return d->session;
}

}

#include "ratingattributesjobmodel.moc"
<commit_msg>if the list if empty then the model rows will also be 0<commit_after>/*
 *   Copyright 2012 Coherent Theory LLC
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2, 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 General Public License for more details
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "ratingattributesjobmodel.h"

#include "ratingattributesjob.h"
#include "session.h"
#include "networkjob.h"

#include <QtCore/QVariant>
#include <QtCore/QDebug>
#include <QtCore/QMetaEnum>
#include <QDebug>
namespace Bodega
{

class RatingAttributesJobModel::Private {
public:
    Private(RatingAttributesJobModel *parent);

    RatingAttributesJobModel *q;
    Session *session;
    void fetchRatingAttributes();
    void ratingAttributesJobFinished(Bodega::NetworkJob *job);
    void assetJobFinished(Bodega::NetworkJob *job);

    QString findAverageRating(const QString ratingAttributeId) const;
    QString findRatingsCount(const QString ratingAttributeId) const;

    QString assetId;
    QList<RatingAttributes> ratingAttributes;
    AssetInfo assetInfo;
};

RatingAttributesJobModel::Private::Private(RatingAttributesJobModel *parent)
    : q(parent),
      session(0)
{
}

void RatingAttributesJobModel::Private::fetchRatingAttributes()
{
    RatingAttributesJob *job = session->listRatingAttributes(assetId);

    q->beginResetModel();
    ratingAttributes.clear();
    assetInfo.clear();
    q->endResetModel();

    connect(job, SIGNAL(jobFinished(Bodega::NetworkJob *)),
            q, SLOT(ratingAttributesJobFinished(Bodega::NetworkJob *)));
}

void RatingAttributesJobModel::Private::ratingAttributesJobFinished(Bodega::NetworkJob *job)
{
    RatingAttributesJob *ratingAttributesJob = qobject_cast<RatingAttributesJob*>(job);

    if (!ratingAttributesJob) {
        return;
    }

    ratingAttributesJob->deleteLater();

    if (ratingAttributesJob->failed()) {
        return;
    }

    ratingAttributes = ratingAttributesJob->ratingAttributes();
    const int begin = 0;
    const int end = qMax(begin, ratingAttributes.count() -1);
    q->beginInsertRows(QModelIndex(), begin, end);

    AssetJob *assetJob = session->asset(assetId, AssetJob::Ratings);
    connect(assetJob, SIGNAL(jobFinished(Bodega::NetworkJob *)),
            q, SLOT(assetJobFinished(Bodega::NetworkJob *)));
}

void RatingAttributesJobModel::Private::assetJobFinished(Bodega::NetworkJob *job)
{
    AssetJob *assetJob = qobject_cast<AssetJob*>(job);

    if (!assetJob) {
        return;
    }

    assetJob->deleteLater();

    if (assetJob->failed()) {
        q->endInsertRows();
        return;
    }
    assetInfo = assetJob->info();
    q->endInsertRows();
}

QString RatingAttributesJobModel::Private::findAverageRating(const QString ratingAttributeId) const
{
    foreach(const AssetInfo::AssetInfoRatings &info, assetInfo.ratings) {
        if (ratingAttributeId == info.attributeId) {
            return info.averageRating;
        }
    }
    return QString();
}

QString RatingAttributesJobModel::Private::findRatingsCount(const QString ratingAttributeId) const
{
    foreach(const AssetInfo::AssetInfoRatings &info, assetInfo.ratings) {
        if (ratingAttributeId == info.attributeId) {
            return info.ratingsCount;
        }
    }
    return QString();
}

RatingAttributesJobModel::RatingAttributesJobModel(QObject *parent)
    : QAbstractItemModel(parent),
      d(new Private(this))
{
    // set the role names based on the values of the DisplayRoles enum for
    //  the sake of QML
    QHash<int, QByteArray> roles;

    QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("DisplayRoles"));
    for (int i = 0; i < e.keyCount(); ++i) {
        roles.insert(e.value(i), e.key(i));
    }
    setRoleNames(roles);

    connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)),
            this, SIGNAL(countChanged()));
    connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)),
            this, SIGNAL(countChanged()));
    connect(this, SIGNAL(modelReset()),
            this, SIGNAL(countChanged()));
    connect(this, SIGNAL(assetIdChanged()),
            this, SLOT(fetchRatingAttributes()));
}

RatingAttributesJobModel::~RatingAttributesJobModel()
{
    delete d;
}

QString RatingAttributesJobModel::assetId() const
{
    return d->assetId;
}

void RatingAttributesJobModel::setAssetId(const QString& assetId)
{
    d->assetId = assetId;
    emit assetIdChanged();
}

int RatingAttributesJobModel::columnCount(const QModelIndex &parent) const
{
    return 1;
}

QVariant RatingAttributesJobModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() >= d->ratingAttributes.count()) {
        return QVariant();
    }

    switch (role) {
        case Name: {
            return d->ratingAttributes.at(index.row()).name;
        }
        case LowDesc: {
            return d->ratingAttributes.at(index.row()).lowDesc;
        }
        case HighDesc: {
            return d->ratingAttributes.at(index.row()).highDesc;
        }
        case AssetType: {
            return d->ratingAttributes.at(index.row()).assetType;
        }
        case RatingsCount: {
            return d->findRatingsCount(d->ratingAttributes.at(index.row()).id);
        }
        case AverageRating: {
            return d->findAverageRating(d->ratingAttributes.at(index.row()).id);
        }

        default: {
            return QVariant();
        }
    }
}

Qt::ItemFlags RatingAttributesJobModel::flags(const QModelIndex &index) const
{
    if (index.isValid()) {
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    } else {
        return Qt::NoItemFlags;
    }
}

bool RatingAttributesJobModel::hasChildren(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return false;
}

QVariant RatingAttributesJobModel::headerData(int section, Qt::Orientation orientation,
                           int role) const
{
    return QVariant();
}

QModelIndex RatingAttributesJobModel::index(int row, int column, const QModelIndex &parent) const
{
    if (column > 0) {
        return QModelIndex();
    }

    if (row < 0 || row >= d->ratingAttributes.count()) {
        return QModelIndex();
    }

    return createIndex(row, column);
}

QMap<int, QVariant> RatingAttributesJobModel::itemData(const QModelIndex &index) const
{
    return QMap<int, QVariant>();
}

QModelIndex RatingAttributesJobModel::parent(const QModelIndex &index) const
{
    return QModelIndex();
}

int RatingAttributesJobModel::rowCount(const QModelIndex &parent) const
{
    return d->ratingAttributes.size();
}

void RatingAttributesJobModel::setSession(Session *session)
{
    if (session == d->session) {
        return;
    }

    if (d->session) {
        //not connected directly, so disconnect everything
        d->session->disconnect(this);
    }
    d->session = session;

    if (!d->session) {
        return;
    }
}

Session *RatingAttributesJobModel::session() const
{
    return d->session;
}

}

#include "ratingattributesjobmodel.moc"
<|endoftext|>


<commit_before>#include <aleph/persistenceDiagrams/Norms.hh>
#include <aleph/persistenceDiagrams/PersistenceDiagram.hh>

#include <aleph/persistentHomology/Calculation.hh>

#include <aleph/topology/Simplex.hh>
#include <aleph/topology/SimplicialComplex.hh>
#include <aleph/topology/io/BipartiteAdjacencyMatrix.hh>

#include <algorithm>
#include <iostream>
#include <limits>
#include <vector>

#include <getopt.h>

int main( int argc, char** argv )
{
  bool minimum           = false;
  bool normalize         = false;
  bool calculateDiagrams = false;

  {
    static option commandLineOptions[] =
    {
      { "minimum"             , no_argument, nullptr, 'm' },
      { "normalize"           , no_argument, nullptr, 'n' },
      { "persistence-diagrams", no_argument, nullptr, 'p' },
      { nullptr               , 0          , nullptr,  0  }
    };

    int option = 0;
    while( ( option = getopt_long( argc, argv, "mnp", commandLineOptions, nullptr ) ) != -1 )
    {
      switch( option )
      {
      case 'm':
        minimum = true;
        break;
      case 'n':
        normalize = true;
        break;
      case 'p':
        calculateDiagrams = true;
        break;
      default:
        break;
      }
    }
  }

  using DataType          = double;
  using VertexType        = unsigned short;
  using Simplex           = aleph::topology::Simplex<DataType, VertexType>;
  using SimplicialComplex = aleph::topology::SimplicialComplex<Simplex>;

  DataType minData = std::numeric_limits<DataType>::max();
  DataType maxData = std::numeric_limits<DataType>::lowest();

  // 1. Read simplicial complexes --------------------------------------

  std::vector<SimplicialComplex> simplicialComplexes;
  simplicialComplexes.reserve( static_cast<unsigned>( argc - optind - 1 ) );

  {
    aleph::topology::io::BipartiteAdjacencyMatrixReader reader;

    if( minimum )
      reader.setAssignMinimumVertexWeight();

    for( int i = optind; i < argc; i++ )
    {
      auto filename = std::string( argv[i] );

      std::cerr << "* Processing " << filename << "...";

      SimplicialComplex K;
      reader( filename, K );

      std::cerr << "finished\n";

      // *Always* determine minimum and maximum weights so that we may
      // report them later on. They are only used for normalization in
      // the persistence diagram calculation step.
      for( auto&& s : K )
      {
        minData = std::min( minData, s.data() );
        maxData = std::max( maxData, s.data() );
      }

      simplicialComplexes.emplace_back( K );
    }
  }

  // 2. Calculate persistent homology ----------------------------------

  for( std::size_t i = 0; i < simplicialComplexes.size(); i++ )
  {
    auto&& K      = simplicialComplexes[i];
    auto diagrams = aleph::calculatePersistenceDiagrams( K );
    auto&& D      = diagrams.front();

    D.removeDiagonal();
    D.removeUnpaired();

    if( normalize )
    {
      using PersistenceDiagram = aleph::PersistenceDiagram<DataType>;
      using Point              = typename PersistenceDiagram::Point;

      // Ensures that points are in [-1:+1]
      std::transform( D.begin(), D.end(), D.begin(),
        [&minData, &maxData] ( const Point& p )
        {
          auto x = p.x();
          auto y = p.y();
          x      = 2 * (x - minData) / (maxData - minData) - 1;
          y      = 2 * (y - minData) / (maxData - minData) - 1;

          return Point( x,y );
        }
      );
    }

    // Determine mode of operation -------------------------------------
    //
    // Several modes of operation exist for this program. They can be
    // set using the flags specified above. At present, the following
    // operations are possible:
    //
    // - Calculate persistence diagrams
    // - Calculate 2-norm of the persistence diagrams

    if( calculateDiagrams )
      std::cout << D << "\n\n";
    else
      std::cout << i << "\t" << aleph::pNorm( D ) << "\n";
  }
}
<commit_msg>Added support for calculating trajectory distances<commit_after>#include <aleph/math/SymmetricMatrix.hh>

#include <aleph/persistenceDiagrams/Distances.hh>
#include <aleph/persistenceDiagrams/Norms.hh>
#include <aleph/persistenceDiagrams/PersistenceDiagram.hh>

#include <aleph/persistentHomology/Calculation.hh>

#include <aleph/topology/Simplex.hh>
#include <aleph/topology/SimplicialComplex.hh>
#include <aleph/topology/io/BipartiteAdjacencyMatrix.hh>

#include <algorithm>
#include <iostream>
#include <limits>
#include <vector>

#include <getopt.h>

int main( int argc, char** argv )
{
  bool minimum               = false;
  bool normalize             = false;
  bool calculateDiagrams     = false;
  bool calculateTrajectories = false;

  {
    static option commandLineOptions[] =
    {
      { "minimum"             , no_argument, nullptr, 'm' },
      { "normalize"           , no_argument, nullptr, 'n' },
      { "persistence-diagrams", no_argument, nullptr, 'p' },
      { "trajectories"        , no_argument, nullptr, 't' },
      { nullptr               , 0          , nullptr,  0  }
    };

    int option = 0;
    while( ( option = getopt_long( argc, argv, "mnpt", commandLineOptions, nullptr ) ) != -1 )
    {
      switch( option )
      {
      case 'm':
        minimum = true;
        break;
      case 'n':
        normalize = true;
        break;
      case 'p':
        calculateDiagrams = true;
        break;
      case 't':
        calculateTrajectories = true;
        break;
      default:
        break;
      }
    }
  }

  using DataType          = double;
  using VertexType        = unsigned short;
  using Simplex           = aleph::topology::Simplex<DataType, VertexType>;
  using SimplicialComplex = aleph::topology::SimplicialComplex<Simplex>;

  DataType minData = std::numeric_limits<DataType>::max();
  DataType maxData = std::numeric_limits<DataType>::lowest();

  // 1. Read simplicial complexes --------------------------------------

  std::vector<SimplicialComplex> simplicialComplexes;
  simplicialComplexes.reserve( static_cast<unsigned>( argc - optind - 1 ) );

  {
    aleph::topology::io::BipartiteAdjacencyMatrixReader reader;

    if( minimum )
      reader.setAssignMinimumVertexWeight();

    for( int i = optind; i < argc; i++ )
    {
      auto filename = std::string( argv[i] );

      std::cerr << "* Processing " << filename << "...";

      SimplicialComplex K;
      reader( filename, K );

      std::cerr << "finished\n";

      // *Always* determine minimum and maximum weights so that we may
      // report them later on. They are only used for normalization in
      // the persistence diagram calculation step.
      for( auto&& s : K )
      {
        minData = std::min( minData, s.data() );
        maxData = std::max( maxData, s.data() );
      }

      simplicialComplexes.emplace_back( K );
    }
  }

  // 2. Calculate persistent homology ----------------------------------

  using Matrix = aleph::math::SymmetricMatrix<double>;

  // Stores the distance matrix for the trajectories of persistence
  // diagrams. This will only be used if the client set the correct
  // flag.
  Matrix trajectoryDistances;
  if( calculateTrajectories )
    trajectoryDistances = Matrix( simplicialComplexes.size() );

  using PersistenceDiagram = aleph::PersistenceDiagram<DataType>;
  using Point              = typename PersistenceDiagram::Point;

  // Stores the zeroth persistence diagram for calculating trajectories
  // later on. This may need to be extended in order to handle diagrams
  // with higher-dimensional features.
  std::vector<PersistenceDiagram> trajectoryDiagrams;
  if( calculateTrajectories )
    trajectoryDiagrams.reserve( simplicialComplexes.size() );

  for( std::size_t i = 0; i < simplicialComplexes.size(); i++ )
  {
    auto&& K      = simplicialComplexes[i];
    auto diagrams = aleph::calculatePersistenceDiagrams( K );
    auto&& D      = diagrams.front();

    D.removeDiagonal();
    D.removeUnpaired();

    if( normalize )
    {
      // Ensures that points are in [-1:+1]
      std::transform( D.begin(), D.end(), D.begin(),
        [&minData, &maxData] ( const Point& p )
        {
          auto x = p.x();
          auto y = p.y();
          x      = 2 * (x - minData) / (maxData - minData) - 1;
          y      = 2 * (y - minData) / (maxData - minData) - 1;

          return Point( x,y );
        }
      );
    }

    // Determine mode of operation -------------------------------------
    //
    // Several modes of operation exist for this program. They can be
    // set using the flags specified above. At present, the following
    // operations are possible:
    //
    // - Calculate persistence diagrams
    // - Calculate persistence diagram trajectories
    // - Calculate 2-norm of the persistence diagrams

    if( calculateDiagrams )
      std::cout << D << "\n\n";
    else if( calculateTrajectories )
      trajectoryDiagrams.push_back( D );
    else
      std::cout << i << "\t" << aleph::pNorm( D ) << "\n";
  }

  // Need to calculate the trajectories afterwards because they require
  // building a database of persistence diagrams.
  if( calculateTrajectories )
  {
    for( std::size_t i = 0; i < trajectoryDiagrams.size(); i++ )
    {
      auto&& Di = trajectoryDiagrams[i];

      for( std::size_t j = i+1; j < trajectoryDiagrams.size(); j++ )
      {
        auto&& Dj = trajectoryDiagrams[j];
        auto dist = aleph::distances::hausdorffDistance(
          Di, Dj
        );

        trajectoryDistances(i,j) = dist;
      }
    }

    // FIXME: replace with proper layout
    std::cout << trajectoryDistances << "\n";
  }
}
<|endoftext|>