<commit_before>/*
 * GatewayReceiver.cpp
 *
 *  Created on: Aug 28, 2012
 *      Author: jwilliams
 */

#include "GatewayReceiver.h"
#include <sstream>
#include "json/json.h"
#include "protocol/AmmoMessages.pb.h"
#include "SerialConfigurationManager.h"
#include "GpsThread.h"
#include <limits>
#include <cmath>

using namespace std;

#define htonll(x) (((int64_t)(ntohl((int32_t)((x << 32) >> 32))) << 32) | (uint32_t)ntohl(((int)(x >> 32))))

#ifdef WIN32
#define atoll(str) _atoi64(str)
#endif

GatewayReceiver::GatewayReceiver(GpsThread *gpsThread) : gpsThread(gpsThread), receivedMessageCount(0), pliIndex(0) {
  // TODO Auto-generated constructor stub
  SerialConfigurationManager *config = SerialConfigurationManager::getInstance();
  pliRelayPerCycle = config->getPliRelayPerCycle();
  pliRelayEnabled = config->getPliRelayEnabled();
  pliRelayNodeName = config->getPliRelayNodeName();
  rangeScale = config->getRangeScale();
  timeScale = config->getTimeScale();
}

GatewayReceiver::~GatewayReceiver() {
  // TODO Auto-generated destructor stub
}

void GatewayReceiver::onPushDataReceived(
    ammo::gateway::GatewayConnector* sender,
    ammo::gateway::PushData& pushData) {
  LOG_DEBUG("Push data received: uri: " << pushData.uri << " type: " << pushData.mimeType);
  LOG_DEBUG("  data: " << pushData.data.c_str());

  if(pushData.mimeType == "ammo/transapps.pli.locations") {
    /*
     * Terse PLI type definition:
     *   Name : string (uint16_t size + UTF-8 data)
     *   Lat  : int32
     *   Lon  : int32
     *   createdTime: uint32; time from json / 1000
     *   GroupPliBlob : blob
     */
    Json::Value root;
    Json::Reader reader;

    //We only parse the c_str component of the data because the data may contain
    //binary data at the end (separated by a null, which is why this works).
    bool parsingSuccessful = reader.parse(pushData.data.c_str(), root);

    if(parsingSuccessful) {
      string name = root["name"].asString();
      int32_t lat = atoi(root["lat"].asString().c_str());
      int32_t lon = atoi(root["lon"].asString().c_str());
      uint32_t time = atoll(root["created"].asString().c_str()) / 1000;
      LOG_DEBUG("XXX name=" << name << " lat=" << lat << " lon=" << lon << " time=" << time);

#ifdef PLI_PASSTHROUGH
      //Pass through PLI messages 1:1
      ostringstream tersePayload;
      appendString(tersePayload, name);
      appendInt32(tersePayload, lat);
      appendInt32(tersePayload, lon);
      appendUInt32(tersePayload, time);

      //TODO: parse and forward PLI relay, too
      std::string pliRelayBlob("\0", 1);
      appendBlob(tersePayload, pliRelayBlob);

      ammo::protocol::MessageWrapper msg;
      msg.set_type(ammo::protocol::MessageWrapper_MessageType_TERSE_MESSAGE);
      msg.set_message_priority(0);
      ammo::protocol::TerseMessage *terseMsg = msg.mutable_terse_message();
      terseMsg->set_mime_type(5);
      terseMsg->set_data(tersePayload.str());

      std::string serializedMessage = msg.SerializeAsString();
      //we need a pointer to this data, so create a new string and copy it
      std::string *messageToSend = new std::string(serializedMessage);
      addReceivedMessage(messageToSend);
#else
      //Use PLI relay to pass through PLI
      PliInfo location;
      location.lat = lat;
      location.lon = lon;
      location.createdTime = time;

      pliMapMutex.acquire();
      pliMap[name] = location;
      pliMapMutex.release();
#endif
    } else {
      LOG_ERROR("Couldn't parse PLI data from gateway");
    }
  } else if (pushData.mimeType == "ammo/transapps.chat.message_groupAll") {
    /* Terse Chat type definition:
     *   Originator : string
     *   Text : string
     *   Created Date : int64
     */
    Json::Value root;
    Json::Reader reader;

    //We only parse the c_str component of the data because the data may contain
    //binary data at the end (separated by a null, which is why this works).
    bool parsingSuccessful = reader.parse(pushData.data.c_str(), root);

    if(parsingSuccessful) {
      string originator = root["originator"].asString();
      string text = root["text"].asString();
      int64_t time = atoll(root["created_date"].asString().c_str());

      ostringstream tersePayload;
      appendString(tersePayload, originator);
      appendString(tersePayload, text);
      appendInt64(tersePayload, time);

      ammo::protocol::MessageWrapper msg;
      msg.set_type(ammo::protocol::MessageWrapper_MessageType_TERSE_MESSAGE);
      msg.set_message_priority(0);
      ammo::protocol::TerseMessage *terseMsg = msg.mutable_terse_message();
      terseMsg->set_mime_type(4);
      terseMsg->set_data(tersePayload.str());

      std::string serializedMessage = msg.SerializeAsString();
      //we need a pointer to this data, so create a new string and copy it
      std::string *messageToSend = new std::string(serializedMessage);
      addReceivedMessage(messageToSend);
    } else {
      LOG_ERROR("Couldn't parse Chat data from gateway");
    }
  }
}

void GatewayReceiver::addPli(std::string &username, int32_t lat, int32_t lon, int32_t createdTime) {
  //this method is only used for relay of PLI from serial; the pliRelayEnabled flag only disables that
  if(pliRelayEnabled) {
    PliInfo location;
    location.lat = lat;
    location.lon = lon;
    location.createdTime = createdTime;

    pliMapMutex.acquire();
    pliMap[username] = location;
    pliMapMutex.release();
  }
}

void GatewayReceiver::appendString(ostringstream &stream, const std::string &str) {
  if(str.length() > numeric_limits<uint16_t>::max()) {
    LOG_WARN("String too long, putting zero length string instead");
    appendUInt16(stream, 0);
  } else {
    appendUInt16(stream,str.length());
    stream << str;
  }
}

void GatewayReceiver::appendInt64(ostringstream &stream, const int64_t val) {
  int64_t networkVal = htonll(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendInt32(ostringstream &stream, const int32_t val) {
  int32_t networkVal = htonl(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendUInt32(ostringstream &stream, const uint32_t val) {
  uint32_t networkVal = htonl(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendInt16(ostringstream &stream, const int16_t val) {
  int16_t networkVal = htons(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendInt8(ostringstream &stream, const int8_t val) {
  stream.write(reinterpret_cast<const char *>(&val), sizeof(val));
}

void GatewayReceiver::appendUInt16(ostringstream &stream, const uint16_t val) {
  uint16_t networkVal = htons(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendBlob(ostringstream &stream, const std::string &blob) {
  //blob format is currently exactly the same as a string, so treat it as such
  appendString(stream, blob);
}

std::string GatewayReceiver::getNextPliRelayPacket() {
  //get the next n locations to be sent
  pliMapMutex.acquire();
  PliMap tempPliMap;

  //get iterator to the first item that's supposed to be sent
  PliMap::iterator pliMapIt = pliMap.begin();
  if(pliMapIt == pliMap.end()) {
    //empty map; don't send anything
    pliMapMutex.release();
    return "";
  }

  for(int i = 0; i < pliIndex; i++) {
    pliMapIt++; //nothing ever deletes from the list--  so we don't need to worry about hitting the end here
  }

  double latDouble = 0;
  double lonDouble = 0;

  if(gpsThread->getPosition(latDouble, lonDouble) == false) {
    LOG_WARN("Can't forward PLI; no GPS lock");
    return "";
  }

  int32_t baseLat = (int) (latDouble * 1e6);
  int32_t baseLon = (int) (lonDouble * 1e6);
  time_t baseTime = time(NULL);

  //And prepare the PLI packet
  ostringstream tersePayload;
  appendString(tersePayload, pliRelayNodeName);
  appendInt32(tersePayload, baseLat);
  appendInt32(tersePayload, baseLon);
  appendUInt32(tersePayload, baseTime);

  ostringstream relayBlob;

  int8_t deltaCount = 0;

  PliMap::iterator baseObject = pliMapIt;

  while(deltaCount < pliRelayPerCycle) {
    int32_t dLat = (baseLat - pliMapIt->second.lat) / rangeScale;
    int32_t dLon = (baseLon - pliMapIt->second.lon) / rangeScale;
    int32_t dTime = (baseTime - pliMapIt->second.createdTime) / timeScale;
    if(numeric_limits<int16_t>::min() <= dLat && dLat <= numeric_limits<int16_t>::max() &&
        numeric_limits<int16_t>::min() <= dLon && dLon <= numeric_limits<int16_t>::max() &&
        numeric_limits<int8_t>::min() <= dTime && dTime <= numeric_limits<int8_t>::max()) {
      appendString(relayBlob, pliMapIt->first);
      appendInt16(relayBlob, dLat);
      appendInt16(relayBlob, dLon);
      appendInt8(relayBlob, dTime);
      appendInt8(relayBlob, 1); //TODO: add hop count
      deltaCount++;
    }

    pliMapIt++;
    pliIndex++;
    if(pliMapIt == pliMap.end()) {
      //wrap around to the beginning of the map if we hit the end
      pliMapIt = pliMap.begin();
      pliIndex = 0;
    }

    if(pliMapIt == baseObject) {
      //we've wrapped back around to where we started; don't repeat
      break;
    }
  }

  ostringstream finalRelayBlob;

  appendInt8(finalRelayBlob, deltaCount);
  finalRelayBlob << relayBlob.str();

  appendBlob(tersePayload, finalRelayBlob.str());

  pliMapMutex.release();

  ammo::protocol::MessageWrapper msg;
  msg.set_type(ammo::protocol::MessageWrapper_MessageType_TERSE_MESSAGE);
  msg.set_message_priority(0);
  ammo::protocol::TerseMessage *terseMsg = msg.mutable_terse_message();
  terseMsg->set_mime_type(5);
  terseMsg->set_data(tersePayload.str());

  std::string serializedMessage = msg.SerializeAsString();

  return serializedMessage;
}

std::string *GatewayReceiver::getNextReceivedMessage() {
  std::string *msg = NULL;
  receiveQueueMutex.acquire();
  if(!receiveQueue.empty()) {
    msg = receiveQueue.front();
    receiveQueue.pop();
  }
  receiveQueueMutex.release();

  return msg;
}

void GatewayReceiver::addReceivedMessage(std::string *msg) {
  receiveQueueMutex.acquire();
  receivedMessageCount++;
  receiveQueue.push(msg);
  receiveQueueMutex.release();
}

int GatewayReceiver::getNextMessageSize() {
  int size = 0;
  receiveQueueMutex.acquire();
  if(!receiveQueue.empty()) {
    size = receiveQueue.front()->length();
  }
  receiveQueueMutex.release();

  return size;
}

bool GatewayReceiver::isMessageAvailable() {
  bool result = false;
  receiveQueueMutex.acquire();
  result = !receiveQueue.empty();
  receiveQueueMutex.release();

  return result;
}
<commit_msg>Fix chat serialization for send support<commit_after>/*
 * GatewayReceiver.cpp
 *
 *  Created on: Aug 28, 2012
 *      Author: jwilliams
 */

#include "GatewayReceiver.h"
#include <sstream>
#include "json/json.h"
#include "protocol/AmmoMessages.pb.h"
#include "SerialConfigurationManager.h"
#include "GpsThread.h"
#include <limits>
#include <cmath>
#include <ace/UUID.h>

using namespace std;

#define htonll(x) (((int64_t)(ntohl((int32_t)((x << 32) >> 32))) << 32) | (uint32_t)ntohl(((int)(x >> 32))))

#ifdef WIN32
#define atoll(str) _atoi64(str)
#endif

GatewayReceiver::GatewayReceiver(GpsThread *gpsThread) : gpsThread(gpsThread), receivedMessageCount(0), pliIndex(0) {
  // TODO Auto-generated constructor stub
  SerialConfigurationManager *config = SerialConfigurationManager::getInstance();
  pliRelayPerCycle = config->getPliRelayPerCycle();
  pliRelayEnabled = config->getPliRelayEnabled();
  pliRelayNodeName = config->getPliRelayNodeName();
  rangeScale = config->getRangeScale();
  timeScale = config->getTimeScale();
}

GatewayReceiver::~GatewayReceiver() {
  // TODO Auto-generated destructor stub
}

void GatewayReceiver::onPushDataReceived(
    ammo::gateway::GatewayConnector* sender,
    ammo::gateway::PushData& pushData) {
  LOG_DEBUG("Push data received: uri: " << pushData.uri << " type: " << pushData.mimeType);
  LOG_DEBUG("  data: " << pushData.data.c_str());

  if(pushData.mimeType == "ammo/transapps.pli.locations") {
    /*
     * Terse PLI type definition:
     *   Name : string (uint16_t size + UTF-8 data)
     *   Lat  : int32
     *   Lon  : int32
     *   createdTime: uint32; time from json / 1000
     *   GroupPliBlob : blob
     */
    Json::Value root;
    Json::Reader reader;

    //We only parse the c_str component of the data because the data may contain
    //binary data at the end (separated by a null, which is why this works).
    bool parsingSuccessful = reader.parse(pushData.data.c_str(), root);

    if(parsingSuccessful) {
      string name = root["name"].asString();
      int32_t lat = atoi(root["lat"].asString().c_str());
      int32_t lon = atoi(root["lon"].asString().c_str());
      uint32_t time = atoll(root["created"].asString().c_str()) / 1000;
      LOG_DEBUG("XXX name=" << name << " lat=" << lat << " lon=" << lon << " time=" << time);

#ifdef PLI_PASSTHROUGH
      //Pass through PLI messages 1:1
      ostringstream tersePayload;
      appendString(tersePayload, name);
      appendInt32(tersePayload, lat);
      appendInt32(tersePayload, lon);
      appendUInt32(tersePayload, time);

      //TODO: parse and forward PLI relay, too
      std::string pliRelayBlob("\0", 1);
      appendBlob(tersePayload, pliRelayBlob);

      ammo::protocol::MessageWrapper msg;
      msg.set_type(ammo::protocol::MessageWrapper_MessageType_TERSE_MESSAGE);
      msg.set_message_priority(0);
      ammo::protocol::TerseMessage *terseMsg = msg.mutable_terse_message();
      terseMsg->set_mime_type(5);
      terseMsg->set_data(tersePayload.str());

      std::string serializedMessage = msg.SerializeAsString();
      //we need a pointer to this data, so create a new string and copy it
      std::string *messageToSend = new std::string(serializedMessage);
      addReceivedMessage(messageToSend);
#else
      //Use PLI relay to pass through PLI
      PliInfo location;
      location.lat = lat;
      location.lon = lon;
      location.createdTime = time;

      pliMapMutex.acquire();
      pliMap[name] = location;
      pliMapMutex.release();
#endif
    } else {
      LOG_ERROR("Couldn't parse PLI data from gateway");
    }
  } else if (pushData.mimeType == "ammo/transapps.chat.message_groupAll") {
    /* Terse Chat type definition:
     *   Originator : string
     *   Text : string
     *   Created Date : int64
     */
    Json::Value root;
    Json::Reader reader;

    //We only parse the c_str component of the data because the data may contain
    //binary data at the end (separated by a null, which is why this works).
    bool parsingSuccessful = reader.parse(pushData.data.c_str(), root);

    if(parsingSuccessful) {
      string originator = root["originator"].asString();
      string text = root["text"].asString();
      int64_t time = atoll(root["created_date"].asString().c_str());
      ACE_Utils::UUID *uuid = ACE_Utils::UUID_GENERATOR::instance ()->generate_UUID ();

      ostringstream tersePayload;
      appendString(tersePayload, string(uuid->to_string()->c_str()));
      appendString(tersePayload, originator);
      appendString(tersePayload, text);
      appendInt32(tersePayload, time / 1000);

      ammo::protocol::MessageWrapper msg;
      msg.set_type(ammo::protocol::MessageWrapper_MessageType_TERSE_MESSAGE);
      msg.set_message_priority(0);
      ammo::protocol::TerseMessage *terseMsg = msg.mutable_terse_message();
      terseMsg->set_mime_type(4);
      terseMsg->set_data(tersePayload.str());

      std::string serializedMessage = msg.SerializeAsString();
      //we need a pointer to this data, so create a new string and copy it
      std::string *messageToSend = new std::string(serializedMessage);
      addReceivedMessage(messageToSend);
    } else {
      LOG_ERROR("Couldn't parse Chat data from gateway");
    }
  }
}

void GatewayReceiver::addPli(std::string &username, int32_t lat, int32_t lon, int32_t createdTime) {
  //this method is only used for relay of PLI from serial; the pliRelayEnabled flag only disables that
  if(pliRelayEnabled) {
    PliInfo location;
    location.lat = lat;
    location.lon = lon;
    location.createdTime = createdTime;

    pliMapMutex.acquire();
    pliMap[username] = location;
    pliMapMutex.release();
  }
}

void GatewayReceiver::appendString(ostringstream &stream, const std::string &str) {
  if(str.length() > numeric_limits<uint16_t>::max()) {
    LOG_WARN("String too long, putting zero length string instead");
    appendUInt16(stream, 0);
  } else {
    appendUInt16(stream,str.length());
    stream << str;
  }
}

void GatewayReceiver::appendInt64(ostringstream &stream, const int64_t val) {
  int64_t networkVal = htonll(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendInt32(ostringstream &stream, const int32_t val) {
  int32_t networkVal = htonl(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendUInt32(ostringstream &stream, const uint32_t val) {
  uint32_t networkVal = htonl(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendInt16(ostringstream &stream, const int16_t val) {
  int16_t networkVal = htons(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendInt8(ostringstream &stream, const int8_t val) {
  stream.write(reinterpret_cast<const char *>(&val), sizeof(val));
}

void GatewayReceiver::appendUInt16(ostringstream &stream, const uint16_t val) {
  uint16_t networkVal = htons(val);
  stream.write(reinterpret_cast<char *>(&networkVal), sizeof(val));
}

void GatewayReceiver::appendBlob(ostringstream &stream, const std::string &blob) {
  //blob format is currently exactly the same as a string, so treat it as such
  appendString(stream, blob);
}

std::string GatewayReceiver::getNextPliRelayPacket() {
  //get the next n locations to be sent
  pliMapMutex.acquire();
  PliMap tempPliMap;

  //get iterator to the first item that's supposed to be sent
  PliMap::iterator pliMapIt = pliMap.begin();
  if(pliMapIt == pliMap.end()) {
    //empty map; don't send anything
    pliMapMutex.release();
    return "";
  }

  for(int i = 0; i < pliIndex; i++) {
    pliMapIt++; //nothing ever deletes from the list--  so we don't need to worry about hitting the end here
  }

  double latDouble = 0;
  double lonDouble = 0;

  if(gpsThread->getPosition(latDouble, lonDouble) == false) {
    LOG_WARN("Can't forward PLI; no GPS lock");
    return "";
  }

  int32_t baseLat = (int) (latDouble * 1e6);
  int32_t baseLon = (int) (lonDouble * 1e6);
  time_t baseTime = time(NULL);

  //And prepare the PLI packet
  ostringstream tersePayload;
  appendString(tersePayload, pliRelayNodeName);
  appendInt32(tersePayload, baseLat);
  appendInt32(tersePayload, baseLon);
  appendUInt32(tersePayload, baseTime);

  ostringstream relayBlob;

  int8_t deltaCount = 0;

  PliMap::iterator baseObject = pliMapIt;

  while(deltaCount < pliRelayPerCycle) {
    int32_t dLat = (baseLat - pliMapIt->second.lat) / rangeScale;
    int32_t dLon = (baseLon - pliMapIt->second.lon) / rangeScale;
    int32_t dTime = (baseTime - pliMapIt->second.createdTime) / timeScale;
    if(numeric_limits<int16_t>::min() <= dLat && dLat <= numeric_limits<int16_t>::max() &&
        numeric_limits<int16_t>::min() <= dLon && dLon <= numeric_limits<int16_t>::max() &&
        numeric_limits<int8_t>::min() <= dTime && dTime <= numeric_limits<int8_t>::max()) {
      appendString(relayBlob, pliMapIt->first);
      appendInt16(relayBlob, dLat);
      appendInt16(relayBlob, dLon);
      appendInt8(relayBlob, dTime);
      appendInt8(relayBlob, 1); //TODO: add hop count
      deltaCount++;
    }

    pliMapIt++;
    pliIndex++;
    if(pliMapIt == pliMap.end()) {
      //wrap around to the beginning of the map if we hit the end
      pliMapIt = pliMap.begin();
      pliIndex = 0;
    }

    if(pliMapIt == baseObject) {
      //we've wrapped back around to where we started; don't repeat
      break;
    }
  }

  ostringstream finalRelayBlob;

  appendInt8(finalRelayBlob, deltaCount);
  finalRelayBlob << relayBlob.str();

  appendBlob(tersePayload, finalRelayBlob.str());

  pliMapMutex.release();

  ammo::protocol::MessageWrapper msg;
  msg.set_type(ammo::protocol::MessageWrapper_MessageType_TERSE_MESSAGE);
  msg.set_message_priority(0);
  ammo::protocol::TerseMessage *terseMsg = msg.mutable_terse_message();
  terseMsg->set_mime_type(5);
  terseMsg->set_data(tersePayload.str());

  std::string serializedMessage = msg.SerializeAsString();

  return serializedMessage;
}

std::string *GatewayReceiver::getNextReceivedMessage() {
  std::string *msg = NULL;
  receiveQueueMutex.acquire();
  if(!receiveQueue.empty()) {
    msg = receiveQueue.front();
    receiveQueue.pop();
  }
  receiveQueueMutex.release();

  return msg;
}

void GatewayReceiver::addReceivedMessage(std::string *msg) {
  receiveQueueMutex.acquire();
  receivedMessageCount++;
  receiveQueue.push(msg);
  receiveQueueMutex.release();
}

int GatewayReceiver::getNextMessageSize() {
  int size = 0;
  receiveQueueMutex.acquire();
  if(!receiveQueue.empty()) {
    size = receiveQueue.front()->length();
  }
  receiveQueueMutex.release();

  return size;
}

bool GatewayReceiver::isMessageAvailable() {
  bool result = false;
  receiveQueueMutex.acquire();
  result = !receiveQueue.empty();
  receiveQueueMutex.release();

  return result;
}
<|endoftext|>


<commit_before>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#pragma once

#ifndef ORK_ORK_HPP
#	error This header can only be included from ork/ork.hpp!
#endif

#include"ork/detail/config.hpp"
#include"ork/detail/text.hpp"
#include"ork/detail/exception.hpp"

namespace ork {

#ifndef ORK_WARN_
#	define ORK_WARN_(MSG) ORK_CAT3(ORK_FLOC, ORK(": Warning: "), ORK(MSG))
#	if ORK_MSC
#		define ORK_VSWARNING(MSG) message(ORK_WARN_(MSG))
#	else
#		define ORK_VSWARNING(MSG)
#	endif
#endif

//Make the code execute as one statement; used because it could be more exotic(per-platform) in the future.
#define ORK_STMT(CODE) {CODE} //do{CODE}while(false);

#ifndef ORK_DEPRECATED
#	if ORK_GCC
#		define ORK_DEPRECATED __attribute__((deprecated))
#	else
#		define ORK_DEPRECATED
#	endif
#endif

#ifndef ORK_INLINE
#	if ORK_MSC
#		define ORK_INLINE __forceinline
#	elif ORK_GCC
#		define ORK_INLINE __attribute__((always_inline)) inline
#	else
#		error Compiler not supported
#	endif
#endif

#if ORK_GCC || ORK_MSC > 1800
#define ORK_NO_EXCEPT noexcept
#define ORK_CONSTEXPR constexpr
#else
#define ORK_NO_EXCEPT//throw()
#define ORK_CONSTEXPR
#endif

#define ORK_UNREACHABLE ORK_THROW(ORK("Unreachable"));

/***
Begin Looping section.
Macros to simplify basic loops.
This may seem like overkill,but the loop headers are easier to type,
less error prone,and consistently optimized.
***/
#define LOOP_(LIMIT,INDEX)for(size_t INDEX=0;INDEX!=LIMIT;++INDEX)
#define LOOPI(LIMIT)LOOP_(LIMIT,i)
#define LOOPJ(LIMIT)LOOP_(LIMIT,j)
#define LOOPK(LIMIT)LOOP_(LIMIT,k)
#define LOOPL(LIMIT)LOOP_(LIMIT,l)

#define LOOPR(LIMIT,INDEX)for(size_t INDEX=LIMIT;INDEX--!=0;/*Do Nothing*/)
#define LOOPRI(LIMIT)LOOPR(LIMIT,i)
#define LOOPRJ(LIMIT)LOOPR(LIMIT,j)
#define LOOPRK(LIMIT)LOOPR(LIMIT,k)
#define LOOPRL(LIMIT)LOOPR(LIMIT,l)

/*Loops over vectors, the limit variable must be obviously constant for the compiler to vectorize anything.*/
#define LOOPV(SERIES,INDEX)for(size_t INDEX=0,limit##INDEX=SERIES.size();INDEX!=limit##INDEX;++INDEX)
#define LOOPVI(SERIES)LOOPV(SERIES,i)
#define LOOPVJ(SERIES)LOOPV(SERIES,j)
#define LOOPVK(SERIES)LOOPV(SERIES,k)
#define LOOPVL(SERIES)LOOPV(SERIES,l)

#define ORK_EMPTY_ /*Empty*/

/*
Copy and move semantics
*/

#define ORK_COPY_(TYPE)\
	TYPE(const TYPE&)=default;\
	TYPE&operator=(const TYPE&)=default;

#if ORK_GCC || ORK_MSC > 1700//VS 2013 or later
	#define ORK_NO_COPY_(TYPE)\
			TYPE(const TYPE&)=delete;\
			TYPE&operator=(const TYPE&)=delete;
	#define ORK_NO_MOVE_(TYPE)\
			TYPE(TYPE&&)=delete;\
			TYPE&operator=(TYPE&&)=delete;
	#define ORK_MOVE_(TYPE)\
		TYPE(TYPE&&)=default;\
		TYPE&operator=(TYPE&&)=default;
#else
	#define ORK_NO_COPY_(TYPE)\
			private:\
				TYPE(const TYPE&);\
				TYPE&operator=(const TYPE&);
	#define ORK_NO_MOVE_(TYPE) ORK_EMPTY_
	#define ORK_MOVE_(TYPE) ORK_EMPTY_
#endif

#define ORK_NON_COPYABLE(TYPE) ORK_NO_COPY_(TYPE) ORK_NO_MOVE_(TYPE)
#if ORK_GCC || ORK_MSC > 1700//VS 2013 or later
#define ORK_MOVE_ONLY(TYPE) ORK_NO_COPY_(TYPE) ORK_MOVE_(TYPE)
#define ORK_MOVEABLE(TYPE) ORK_COPY_(TYPE) ORK_MOVE_(TYPE)
#endif

/*
The macros below provide more control over construction, including insertion of
code in both the constructor and assignment operator.
These macros also support VS 2012.
*/
//Construction
#define ORK_CMOVE_(M) M(std::move(rhs.M))
#define ORK_CMOVE_1_(M1)				ORK_CMOVE_(M1)
#define ORK_CMOVE_2_(M1,M2)				ORK_CMOVE_1_(M1),			ORK_CMOVE_(M2)
#define ORK_CMOVE_3_(M1,M2,M3)			ORK_CMOVE_2_(M1,M2),		ORK_CMOVE_(M3)
#define ORK_CMOVE_4_(M1,M2,M3,M4)		ORK_CMOVE_3_(M1,M2,M3),		ORK_CMOVE_(M4)
#define ORK_CMOVE_5_(M1,M2,M3,M4,M5)	ORK_CMOVE_4_(M1,M2,M3,M4),	ORK_CMOVE_(M5)

//Assignment
#define ORK_AMOVE_(M) M=std::move(rhs.M);
#define ORK_AMOVE_1_(M1)				ORK_AMOVE_(M1)
#define ORK_AMOVE_2_(M1,M2)				ORK_AMOVE_1_(M1)			ORK_AMOVE_(M2)
#define ORK_AMOVE_3_(M1,M2,M3)			ORK_AMOVE_2_(M1,M2)			ORK_AMOVE_(M3)
#define ORK_AMOVE_4_(M1,M2,M3,M4)		ORK_AMOVE_3_(M1,M2,M3)		ORK_AMOVE_(M4)
#define ORK_AMOVE_5_(M1,M2,M3,M4,M5)	ORK_AMOVE_4_(M1,M2,M3,M4)	ORK_AMOVE_(M5)

/*
This constructor somewhat surreptitiously inserts noexcept
but a throwing move constructor is pointless anyway
*/
#define ORK_MOVE_CONSTRUCT_(TYPE,CODE)\
public:\
	TYPE(TYPE&&rhs)ORK_NO_EXCEPT:CODE{
/*Constructor body here*/
#define ORK_MOVE_ASSIGN_(TYPE,CODE)\
	}\
public:\
	TYPE&operator=(TYPE&&rhs)ORK_NO_EXCEPT{\
		if(this!=&rhs){\
			CODE
/*Assignment body here*/
#define ORK_MOVE_END_\
		}\
		return *this;\
	}

/*
These macros wrap up functionality.
The first inserts the same code in the constuctor and assigment operator.
The second inserts no code.
*/
#define ORK_MOVE_CON1_(TYPE,M1)				ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_1_(M1))
#define ORK_MOVE_CON2_(TYPE,M1,M2)			ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_2_(M1,M2))
#define ORK_MOVE_CON3_(TYPE,M1,M2,M3)		ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_3_(M1,M2,M3))
#define ORK_MOVE_CON4_(TYPE,M1,M2,M3,M4)	ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_4_(M1,M2,M3,M4))
#define ORK_MOVE_CON5_(TYPE,M1,M2,M3,M4,M5)	ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_5_(M1,M2,M3,M4,M5))

#define ORK_MOVE_OP1_(TYPE,M1)				ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_1_(M1))
#define ORK_MOVE_OP2_(TYPE,M1,M2)			ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_2_(M1,M2))
#define ORK_MOVE_OP3_(TYPE,M1,M2,M3)		ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_3_(M1,M2,M3))
#define ORK_MOVE_OP4_(TYPE,M1,M2,M3,M4)		ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_4_(M1,M2,M3,M4))
#define ORK_MOVE_OP5_(TYPE,M1,M2,M3,M4,M5)	ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_5_(M1,M2,M3,M4,M5))

#define ORK_MOVE_1_(TYPE,M1,CODE)				ORK_MOVE_CON1_(TYPE,M1)				CODE ORK_MOVE_OP1_(TYPE,M1)				CODE ORK_MOVE_END_
#define ORK_MOVE_2_(TYPE,M1,M2,CODE)			ORK_MOVE_CON2_(TYPE,M1,M2)			CODE ORK_MOVE_OP2_(TYPE,M1,M2)			CODE ORK_MOVE_END_
#define ORK_MOVE_3_(TYPE,M1,M2,M3,CODE)			ORK_MOVE_CON3_(TYPE,M1,M2,M3)		CODE ORK_MOVE_OP3_(TYPE,M1,M2,M3)		CODE ORK_MOVE_END_
#define ORK_MOVE_4_(TYPE,M1,M2,M3,M4,CODE)		ORK_MOVE_CON4_(TYPE,M1,M2,M3,M4)	CODE ORK_MOVE_OP4_(TYPE,M1,M2,M3,M4)	CODE ORK_MOVE_END_
#define ORK_MOVE_5_(TYPE,M1,M2,M3,M4,M5,CODE)	ORK_MOVE_CON5_(TYPE,M1,M2,M3,M4,M5)	CODE ORK_MOVE_OP5_(TYPE,M1,M2,M3,M4,M5)	CODE ORK_MOVE_END_

#define ORK_MOVEABLE_1(TYPE,M1)				ORK_COPY_(TYPE) ORK_MOVE_1_(TYPE,M1,ORK_EMPTY_)
#define ORK_MOVEABLE_2(TYPE,M1,M2)			ORK_COPY_(TYPE) ORK_MOVE_2_(TYPE,M1,M2,ORK_EMPTY_)
#define ORK_MOVEABLE_3(TYPE,M1,M2,M3)		ORK_COPY_(TYPE) ORK_MOVE_3_(TYPE,M1,M2,M3,ORK_EMPTY_)
#define ORK_MOVEABLE_4(TYPE,M1,M2,M3,M4)	ORK_COPY_(TYPE) ORK_MOVE_4_(TYPE,M1,M2,M3,M4,ORK_EMPTY_)
#define ORK_MOVEABLE_5(TYPE,M1,M2,M3,M4,M5)	ORK_COPY_(TYPE) ORK_MOVE_5_(TYPE,M1,M2,M3,M4,M5,ORK_EMPTY_)

#define ORK_MOVE_ONLY_1(TYPE,M1)				ORK_NO_COPY_(TYPE) ORK_MOVE_1_(TYPE,M1,ORK_EMPTY_)
#define ORK_MOVE_ONLY_2(TYPE,M1,M2)				ORK_NO_COPY_(TYPE) ORK_MOVE_2_(TYPE,M1,M2,ORK_EMPTY_)
#define ORK_MOVE_ONLY_3(TYPE,M1,M2,M3)			ORK_NO_COPY_(TYPE) ORK_MOVE_3_(TYPE,M1,M2,M3,ORK_EMPTY_)
#define ORK_MOVE_ONLY_4(TYPE,M1,M2,M3,M4)		ORK_NO_COPY_(TYPE) ORK_MOVE_4_(TYPE,M1,M2,M3,M4,ORK_EMPTY_)
#define ORK_MOVE_ONLY_5(TYPE,M1,M2,M3,M4,M5)	ORK_NO_COPY_(TYPE) ORK_MOVE_5_(TYPE,M1,M2,M3,M4,M5,ORK_EMPTY_)

}//namespace ork
<commit_msg>Corrected MSC version for default move constructor<commit_after>/*
This file is part of the ORK library.
Full copyright and license terms can be found in the LICENSE.txt file.
*/
#pragma once

#ifndef ORK_ORK_HPP
#	error This header can only be included from ork/ork.hpp!
#endif

#include"ork/detail/config.hpp"
#include"ork/detail/text.hpp"
#include"ork/detail/exception.hpp"

namespace ork {

#ifndef ORK_WARN_
#	define ORK_WARN_(MSG) ORK_CAT3(ORK_FLOC, ORK(": Warning: "), ORK(MSG))
#	if ORK_MSC
#		define ORK_VSWARNING(MSG) message(ORK_WARN_(MSG))
#	else
#		define ORK_VSWARNING(MSG)
#	endif
#endif

//Make the code execute as one statement; used because it could be more exotic(per-platform) in the future.
#define ORK_STMT(CODE) {CODE} //do{CODE}while(false);

#ifndef ORK_DEPRECATED
#	if ORK_GCC
#		define ORK_DEPRECATED __attribute__((deprecated))
#	else
#		define ORK_DEPRECATED
#	endif
#endif

#ifndef ORK_INLINE
#	if ORK_MSC
#		define ORK_INLINE __forceinline
#	elif ORK_GCC
#		define ORK_INLINE __attribute__((always_inline)) inline
#	else
#		error Compiler not supported
#	endif
#endif

#if ORK_GCC || ORK_MSC > 1800
#	define ORK_NO_EXCEPT noexcept
#	define ORK_CONSTEXPR constexpr
#else
#	define ORK_NO_EXCEPT//throw()
#	define ORK_CONSTEXPR
#endif

#define ORK_UNREACHABLE ORK_THROW(ORK("Unreachable"));

/***
Begin Looping section.
Macros to simplify basic loops.
This may seem like overkill,but the loop headers are easier to type,
less error prone,and consistently optimized.
***/
#define LOOP_(LIMIT,INDEX)for(size_t INDEX=0;INDEX!=LIMIT;++INDEX)
#define LOOPI(LIMIT)LOOP_(LIMIT,i)
#define LOOPJ(LIMIT)LOOP_(LIMIT,j)
#define LOOPK(LIMIT)LOOP_(LIMIT,k)
#define LOOPL(LIMIT)LOOP_(LIMIT,l)

#define LOOPR(LIMIT,INDEX)for(size_t INDEX=LIMIT;INDEX--!=0;/*Do Nothing*/)
#define LOOPRI(LIMIT)LOOPR(LIMIT,i)
#define LOOPRJ(LIMIT)LOOPR(LIMIT,j)
#define LOOPRK(LIMIT)LOOPR(LIMIT,k)
#define LOOPRL(LIMIT)LOOPR(LIMIT,l)

/*Loops over vectors, the limit variable must be obviously constant for the compiler to vectorize anything.*/
#define LOOPV(SERIES,INDEX)for(size_t INDEX=0,limit##INDEX=SERIES.size();INDEX!=limit##INDEX;++INDEX)
#define LOOPVI(SERIES)LOOPV(SERIES,i)
#define LOOPVJ(SERIES)LOOPV(SERIES,j)
#define LOOPVK(SERIES)LOOPV(SERIES,k)
#define LOOPVL(SERIES)LOOPV(SERIES,l)

#define ORK_EMPTY_ /*Empty*/

/*
Copy and move semantics
*/

#define ORK_COPY_(TYPE)\
	TYPE(const TYPE&)=default;\
	TYPE&operator=(const TYPE&)=default;

#if ORK_GCC || ORK_MSC > 1800//VS 2015 or later
	#define ORK_NO_COPY_(TYPE)\
			TYPE(const TYPE&)=delete;\
			TYPE&operator=(const TYPE&)=delete;
	#define ORK_NO_MOVE_(TYPE)\
			TYPE(TYPE&&)=delete;\
			TYPE&operator=(TYPE&&)=delete;
	#define ORK_MOVE_(TYPE)\
		TYPE(TYPE&&)=default;\
		TYPE&operator=(TYPE&&)=default;
#else
	#define ORK_NO_COPY_(TYPE)\
			private:\
				TYPE(const TYPE&);\
				TYPE&operator=(const TYPE&);
	#define ORK_NO_MOVE_(TYPE) ORK_EMPTY_
	#define ORK_MOVE_(TYPE) ORK_EMPTY_
#endif

#define ORK_NON_COPYABLE(TYPE) ORK_NO_COPY_(TYPE) ORK_NO_MOVE_(TYPE)
#if ORK_GCC || ORK_MSC > 1800//VS 2015 or later
#	define ORK_MOVE_ONLY(TYPE) ORK_NO_COPY_(TYPE) ORK_MOVE_(TYPE)
#	define ORK_MOVEABLE(TYPE) ORK_COPY_(TYPE) ORK_MOVE_(TYPE)
#endif

/*
The macros below provide more control over construction, including insertion of
code in both the constructor and assignment operator.
These macros also support VS 2012.
*/
//Construction
#define ORK_CMOVE_(M) M(std::move(rhs.M))
#define ORK_CMOVE_1_(M1)				ORK_CMOVE_(M1)
#define ORK_CMOVE_2_(M1,M2)				ORK_CMOVE_1_(M1),			ORK_CMOVE_(M2)
#define ORK_CMOVE_3_(M1,M2,M3)			ORK_CMOVE_2_(M1,M2),		ORK_CMOVE_(M3)
#define ORK_CMOVE_4_(M1,M2,M3,M4)		ORK_CMOVE_3_(M1,M2,M3),		ORK_CMOVE_(M4)
#define ORK_CMOVE_5_(M1,M2,M3,M4,M5)	ORK_CMOVE_4_(M1,M2,M3,M4),	ORK_CMOVE_(M5)

//Assignment
#define ORK_AMOVE_(M) M=std::move(rhs.M);
#define ORK_AMOVE_1_(M1)				ORK_AMOVE_(M1)
#define ORK_AMOVE_2_(M1,M2)				ORK_AMOVE_1_(M1)			ORK_AMOVE_(M2)
#define ORK_AMOVE_3_(M1,M2,M3)			ORK_AMOVE_2_(M1,M2)			ORK_AMOVE_(M3)
#define ORK_AMOVE_4_(M1,M2,M3,M4)		ORK_AMOVE_3_(M1,M2,M3)		ORK_AMOVE_(M4)
#define ORK_AMOVE_5_(M1,M2,M3,M4,M5)	ORK_AMOVE_4_(M1,M2,M3,M4)	ORK_AMOVE_(M5)

/*
This constructor somewhat surreptitiously inserts noexcept
but a throwing move constructor is pointless anyway
*/
#define ORK_MOVE_CONSTRUCT_(TYPE,CODE)\
public:\
	TYPE(TYPE&&rhs)ORK_NO_EXCEPT:CODE{
/*Constructor body here*/
#define ORK_MOVE_ASSIGN_(TYPE,CODE)\
	}\
public:\
	TYPE&operator=(TYPE&&rhs)ORK_NO_EXCEPT{\
		if(this!=&rhs){\
			CODE
/*Assignment body here*/
#define ORK_MOVE_END_\
		}\
		return *this;\
	}

/*
These macros wrap up functionality.
The first inserts the same code in the constuctor and assigment operator.
The second inserts no code.
*/
#define ORK_MOVE_CON1_(TYPE,M1)				ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_1_(M1))
#define ORK_MOVE_CON2_(TYPE,M1,M2)			ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_2_(M1,M2))
#define ORK_MOVE_CON3_(TYPE,M1,M2,M3)		ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_3_(M1,M2,M3))
#define ORK_MOVE_CON4_(TYPE,M1,M2,M3,M4)	ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_4_(M1,M2,M3,M4))
#define ORK_MOVE_CON5_(TYPE,M1,M2,M3,M4,M5)	ORK_MOVE_CONSTRUCT_(TYPE,ORK_CMOVE_5_(M1,M2,M3,M4,M5))

#define ORK_MOVE_OP1_(TYPE,M1)				ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_1_(M1))
#define ORK_MOVE_OP2_(TYPE,M1,M2)			ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_2_(M1,M2))
#define ORK_MOVE_OP3_(TYPE,M1,M2,M3)		ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_3_(M1,M2,M3))
#define ORK_MOVE_OP4_(TYPE,M1,M2,M3,M4)		ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_4_(M1,M2,M3,M4))
#define ORK_MOVE_OP5_(TYPE,M1,M2,M3,M4,M5)	ORK_MOVE_ASSIGN_(TYPE,ORK_AMOVE_5_(M1,M2,M3,M4,M5))

#define ORK_MOVE_1_(TYPE,M1,CODE)				ORK_MOVE_CON1_(TYPE,M1)				CODE ORK_MOVE_OP1_(TYPE,M1)				CODE ORK_MOVE_END_
#define ORK_MOVE_2_(TYPE,M1,M2,CODE)			ORK_MOVE_CON2_(TYPE,M1,M2)			CODE ORK_MOVE_OP2_(TYPE,M1,M2)			CODE ORK_MOVE_END_
#define ORK_MOVE_3_(TYPE,M1,M2,M3,CODE)			ORK_MOVE_CON3_(TYPE,M1,M2,M3)		CODE ORK_MOVE_OP3_(TYPE,M1,M2,M3)		CODE ORK_MOVE_END_
#define ORK_MOVE_4_(TYPE,M1,M2,M3,M4,CODE)		ORK_MOVE_CON4_(TYPE,M1,M2,M3,M4)	CODE ORK_MOVE_OP4_(TYPE,M1,M2,M3,M4)	CODE ORK_MOVE_END_
#define ORK_MOVE_5_(TYPE,M1,M2,M3,M4,M5,CODE)	ORK_MOVE_CON5_(TYPE,M1,M2,M3,M4,M5)	CODE ORK_MOVE_OP5_(TYPE,M1,M2,M3,M4,M5)	CODE ORK_MOVE_END_

#define ORK_MOVEABLE_1(TYPE,M1)				ORK_COPY_(TYPE) ORK_MOVE_1_(TYPE,M1,ORK_EMPTY_)
#define ORK_MOVEABLE_2(TYPE,M1,M2)			ORK_COPY_(TYPE) ORK_MOVE_2_(TYPE,M1,M2,ORK_EMPTY_)
#define ORK_MOVEABLE_3(TYPE,M1,M2,M3)		ORK_COPY_(TYPE) ORK_MOVE_3_(TYPE,M1,M2,M3,ORK_EMPTY_)
#define ORK_MOVEABLE_4(TYPE,M1,M2,M3,M4)	ORK_COPY_(TYPE) ORK_MOVE_4_(TYPE,M1,M2,M3,M4,ORK_EMPTY_)
#define ORK_MOVEABLE_5(TYPE,M1,M2,M3,M4,M5)	ORK_COPY_(TYPE) ORK_MOVE_5_(TYPE,M1,M2,M3,M4,M5,ORK_EMPTY_)

#define ORK_MOVE_ONLY_1(TYPE,M1)				ORK_NO_COPY_(TYPE) ORK_MOVE_1_(TYPE,M1,ORK_EMPTY_)
#define ORK_MOVE_ONLY_2(TYPE,M1,M2)				ORK_NO_COPY_(TYPE) ORK_MOVE_2_(TYPE,M1,M2,ORK_EMPTY_)
#define ORK_MOVE_ONLY_3(TYPE,M1,M2,M3)			ORK_NO_COPY_(TYPE) ORK_MOVE_3_(TYPE,M1,M2,M3,ORK_EMPTY_)
#define ORK_MOVE_ONLY_4(TYPE,M1,M2,M3,M4)		ORK_NO_COPY_(TYPE) ORK_MOVE_4_(TYPE,M1,M2,M3,M4,ORK_EMPTY_)
#define ORK_MOVE_ONLY_5(TYPE,M1,M2,M3,M4,M5)	ORK_NO_COPY_(TYPE) ORK_MOVE_5_(TYPE,M1,M2,M3,M4,M5,ORK_EMPTY_)

}//namespace ork
<|endoftext|>


<commit_before>// Copyright (c) 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <string>
#include <utility>
#include <vector>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "spirv-tools/optimizer.hpp"
#include "spirv/1.1/spirv.h"

namespace spvtools {
namespace {

using ::testing::ContainerEq;
using ::testing::HasSubstr;

// Return a string that contains the minimum instructions needed to form
// a valid module.  Other instructions can be appended to this string.
std::string Header() {
  return R"(OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
)";
}

TEST(CppInterface, SuccessfulRoundTrip) {
  const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);

  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(input_text, &binary));
  EXPECT_TRUE(binary.size() > 5u);
  EXPECT_EQ(SpvMagicNumber, binary[0]);
  EXPECT_EQ(SpvVersion, binary[1]);

  // This cannot pass validation since %1 is not defined.
  t.SetMessageConsumer([](spv_message_level_t level, const char* source,
                          const spv_position_t& position, const char* message) {
    EXPECT_EQ(SPV_MSG_ERROR, level);
    EXPECT_STREQ("input", source);
    EXPECT_EQ(0u, position.line);
    EXPECT_EQ(0u, position.column);
    EXPECT_EQ(1u, position.index);
    EXPECT_STREQ("ID 1 has not been defined\n  %2 = OpSizeOf %1 %3\n", message);
  });
  EXPECT_FALSE(t.Validate(binary));

  std::string output_text;
  EXPECT_TRUE(t.Disassemble(binary, &output_text));
  EXPECT_EQ(input_text, output_text);
}

TEST(CppInterface, AssembleEmptyModule) {
  std::vector<uint32_t> binary(10, 42);
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  EXPECT_TRUE(t.Assemble("", &binary));
  // We only have the header.
  EXPECT_EQ(5u, binary.size());
  EXPECT_EQ(SpvMagicNumber, binary[0]);
  EXPECT_EQ(SpvVersion, binary[1]);
}

TEST(CppInterface, AssembleOverloads) {
  const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  {
    std::vector<uint32_t> binary;
    EXPECT_TRUE(t.Assemble(input_text, &binary));
    EXPECT_TRUE(binary.size() > 5u);
    EXPECT_EQ(SpvMagicNumber, binary[0]);
    EXPECT_EQ(SpvVersion, binary[1]);
  }
  {
    std::vector<uint32_t> binary;
    EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size(), &binary));
    EXPECT_TRUE(binary.size() > 5u);
    EXPECT_EQ(SpvMagicNumber, binary[0]);
    EXPECT_EQ(SpvVersion, binary[1]);
  }
  {  // Ignore the last newline.
    std::vector<uint32_t> binary;
    EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size() - 1, &binary));
    EXPECT_TRUE(binary.size() > 5u);
    EXPECT_EQ(SpvMagicNumber, binary[0]);
    EXPECT_EQ(SpvVersion, binary[1]);
  }
}

TEST(CppInterface, DisassembleEmptyModule) {
  std::string text(10, 'x');
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  int invocation_count = 0;
  t.SetMessageConsumer(
      [&invocation_count](spv_message_level_t level, const char* source,
                          const spv_position_t& position, const char* message) {
        ++invocation_count;
        EXPECT_EQ(SPV_MSG_ERROR, level);
        EXPECT_STREQ("input", source);
        EXPECT_EQ(0u, position.line);
        EXPECT_EQ(0u, position.column);
        EXPECT_EQ(0u, position.index);
        EXPECT_STREQ("Missing module.", message);
      });
  EXPECT_FALSE(t.Disassemble({}, &text));
  EXPECT_EQ("xxxxxxxxxx", text);  // The original string is unmodified.
  EXPECT_EQ(1, invocation_count);
}

TEST(CppInterface, DisassembleOverloads) {
  const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);

  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(input_text, &binary));

  {
    std::string output_text;
    EXPECT_TRUE(t.Disassemble(binary, &output_text));
    EXPECT_EQ(input_text, output_text);
  }
  {
    std::string output_text;
    EXPECT_TRUE(t.Disassemble(binary.data(), binary.size(), &output_text));
    EXPECT_EQ(input_text, output_text);
  }
}

TEST(CppInterface, SuccessfulValidation) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  int invocation_count = 0;
  t.SetMessageConsumer([&invocation_count](spv_message_level_t, const char*,
                                           const spv_position_t&, const char*) {
    ++invocation_count;
  });

  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(Header(), &binary));
  EXPECT_TRUE(t.Validate(binary));
  EXPECT_EQ(0, invocation_count);
}

TEST(CppInterface, ValidateOverloads) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(Header(), &binary));

  { EXPECT_TRUE(t.Validate(binary)); }
  { EXPECT_TRUE(t.Validate(binary.data(), binary.size())); }
}

TEST(CppInterface, ValidateEmptyModule) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  int invocation_count = 0;
  t.SetMessageConsumer(
      [&invocation_count](spv_message_level_t level, const char* source,
                          const spv_position_t& position, const char* message) {
        ++invocation_count;
        EXPECT_EQ(SPV_MSG_ERROR, level);
        EXPECT_STREQ("input", source);
        EXPECT_EQ(0u, position.line);
        EXPECT_EQ(0u, position.column);
        EXPECT_EQ(0u, position.index);
        EXPECT_STREQ("Invalid SPIR-V magic number.", message);
      });
  EXPECT_FALSE(t.Validate({}));
  EXPECT_EQ(1, invocation_count);
}

// Returns the assembly for a SPIR-V module with a struct declaration
// with the given number of members.
std::string MakeModuleHavingStruct(int num_members) {
  std::stringstream os;
  os << Header();
  os << R"(%1 = OpTypeInt 32 0
           %2 = OpTypeStruct)";
  for (int i = 0; i < num_members; i++) os << " %1";
  return os.str();
}

TEST(CppInterface, ValidateWithOptionsPass) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary));
  const ValidatorOptions opts;

  EXPECT_TRUE(t.Validate(binary.data(), binary.size(), opts));
}

TEST(CppInterface, ValidateWithOptionsFail) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary));
  ValidatorOptions opts;
  opts.SetUniversalLimit(spv_validator_limit_max_struct_members, 9);
  std::stringstream os;
  t.SetMessageConsumer([&os](spv_message_level_t, const char*,
                             const spv_position_t&,
                             const char* message) { os << message; });

  EXPECT_FALSE(t.Validate(binary.data(), binary.size(), opts));
  EXPECT_THAT(
      os.str(),
      HasSubstr(
          "Number of OpTypeStruct members (10) has exceeded the limit (9)"));
}

// Checks that after running the given optimizer |opt| on the given |original|
// source code, we can get the given |optimized| source code.
void CheckOptimization(const std::string& original,
                       const std::string& optimized, const Optimizer& opt) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> original_binary;
  ASSERT_TRUE(t.Assemble(original, &original_binary));

  std::vector<uint32_t> optimized_binary;
  EXPECT_TRUE(opt.Run(original_binary.data(), original_binary.size(),
                      &optimized_binary));

  std::string optimized_text;
  EXPECT_TRUE(t.Disassemble(optimized_binary, &optimized_text));
  EXPECT_EQ(optimized, optimized_text);
}

TEST(CppInterface, OptimizeEmptyModule) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble("", &binary));

  Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  o.RegisterPass(CreateStripDebugInfoPass());

  // Fails to validate.
  EXPECT_FALSE(o.Run(binary.data(), binary.size(), &binary));
}

TEST(CppInterface, OptimizeModifiedModule) {
  Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  o.RegisterPass(CreateStripDebugInfoPass());
  CheckOptimization(Header() + "OpSource GLSL 450", Header(), o);
}

TEST(CppInterface, OptimizeMulitplePasses) {
  std::string original_text = Header() +
                              "OpSource GLSL 450 "
                              "OpDecorate %true SpecId 1 "
                              "%bool = OpTypeBool "
                              "%true = OpSpecConstantTrue %bool";

  Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  o.RegisterPass(CreateStripDebugInfoPass())
      .RegisterPass(CreateFreezeSpecConstantValuePass());

  std::string expected_text = Header() +
                              "%bool = OpTypeBool\n"
                              "%true = OpConstantTrue %bool\n";

  CheckOptimization(original_text, expected_text, o);
}

TEST(CppInterface, OptimizeDoNothingWithPassToken) {
  CreateFreezeSpecConstantValuePass();
  auto token = CreateUnifyConstantPass();
}

TEST(CppInterface, OptimizeReassignPassToken) {
  auto token = CreateNullPass();
  token = CreateStripDebugInfoPass();

  CheckOptimization(
      Header() + "OpSource GLSL 450", Header(),
      Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token)));
}

TEST(CppInterface, OptimizeMoveConstructPassToken) {
  auto token1 = CreateStripDebugInfoPass();
  Optimizer::PassToken token2(std::move(token1));

  CheckOptimization(
      Header() + "OpSource GLSL 450", Header(),
      Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2)));
}

TEST(CppInterface, OptimizeMoveAssignPassToken) {
  auto token1 = CreateStripDebugInfoPass();
  auto token2 = CreateNullPass();
  token2 = std::move(token1);

  CheckOptimization(
      Header() + "OpSource GLSL 450", Header(),
      Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2)));
}

TEST(CppInterface, OptimizeSameAddressForOriginalOptimizedBinary) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  ASSERT_TRUE(t.Assemble(Header() + "OpSource GLSL 450", &binary));

  EXPECT_TRUE(Optimizer(SPV_ENV_UNIVERSAL_1_1)
                  .RegisterPass(CreateStripDebugInfoPass())
                  .Run(binary.data(), binary.size(), &binary));

  std::string optimized_text;
  EXPECT_TRUE(t.Disassemble(binary, &optimized_text));
  EXPECT_EQ(Header(), optimized_text);
}

// TODO(antiagainst): tests for SetMessageConsumer().

}  // namespace
}  // namespace spvtools
<commit_msg>Set expected version number for cpp interface test (#2154)<commit_after>// Copyright (c) 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <string>
#include <utility>
#include <vector>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "spirv-tools/optimizer.hpp"
#include "spirv/1.1/spirv.h"

namespace spvtools {
namespace {

using ::testing::ContainerEq;
using ::testing::HasSubstr;

// Return a string that contains the minimum instructions needed to form
// a valid module.  Other instructions can be appended to this string.
std::string Header() {
  return R"(OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
)";
}

// When we assemble with a target environment of SPIR-V 1.1, we expect
// the following in the module header version word.
const uint32_t kExpectedSpvVersion = 0x10100;

TEST(CppInterface, SuccessfulRoundTrip) {
  const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);

  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(input_text, &binary));
  EXPECT_TRUE(binary.size() > 5u);
  EXPECT_EQ(SpvMagicNumber, binary[0]);
  EXPECT_EQ(kExpectedSpvVersion, binary[1]);

  // This cannot pass validation since %1 is not defined.
  t.SetMessageConsumer([](spv_message_level_t level, const char* source,
                          const spv_position_t& position, const char* message) {
    EXPECT_EQ(SPV_MSG_ERROR, level);
    EXPECT_STREQ("input", source);
    EXPECT_EQ(0u, position.line);
    EXPECT_EQ(0u, position.column);
    EXPECT_EQ(1u, position.index);
    EXPECT_STREQ("ID 1 has not been defined\n  %2 = OpSizeOf %1 %3\n", message);
  });
  EXPECT_FALSE(t.Validate(binary));

  std::string output_text;
  EXPECT_TRUE(t.Disassemble(binary, &output_text));
  EXPECT_EQ(input_text, output_text);
}

TEST(CppInterface, AssembleEmptyModule) {
  std::vector<uint32_t> binary(10, 42);
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  EXPECT_TRUE(t.Assemble("", &binary));
  // We only have the header.
  EXPECT_EQ(5u, binary.size());
  EXPECT_EQ(SpvMagicNumber, binary[0]);
  EXPECT_EQ(kExpectedSpvVersion, binary[1]);
}

TEST(CppInterface, AssembleOverloads) {
  const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  {
    std::vector<uint32_t> binary;
    EXPECT_TRUE(t.Assemble(input_text, &binary));
    EXPECT_TRUE(binary.size() > 5u);
    EXPECT_EQ(SpvMagicNumber, binary[0]);
    EXPECT_EQ(kExpectedSpvVersion, binary[1]);
  }
  {
    std::vector<uint32_t> binary;
    EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size(), &binary));
    EXPECT_TRUE(binary.size() > 5u);
    EXPECT_EQ(SpvMagicNumber, binary[0]);
    EXPECT_EQ(kExpectedSpvVersion, binary[1]);
  }
  {  // Ignore the last newline.
    std::vector<uint32_t> binary;
    EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size() - 1, &binary));
    EXPECT_TRUE(binary.size() > 5u);
    EXPECT_EQ(SpvMagicNumber, binary[0]);
    EXPECT_EQ(kExpectedSpvVersion, binary[1]);
  }
}

TEST(CppInterface, DisassembleEmptyModule) {
  std::string text(10, 'x');
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  int invocation_count = 0;
  t.SetMessageConsumer(
      [&invocation_count](spv_message_level_t level, const char* source,
                          const spv_position_t& position, const char* message) {
        ++invocation_count;
        EXPECT_EQ(SPV_MSG_ERROR, level);
        EXPECT_STREQ("input", source);
        EXPECT_EQ(0u, position.line);
        EXPECT_EQ(0u, position.column);
        EXPECT_EQ(0u, position.index);
        EXPECT_STREQ("Missing module.", message);
      });
  EXPECT_FALSE(t.Disassemble({}, &text));
  EXPECT_EQ("xxxxxxxxxx", text);  // The original string is unmodified.
  EXPECT_EQ(1, invocation_count);
}

TEST(CppInterface, DisassembleOverloads) {
  const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);

  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(input_text, &binary));

  {
    std::string output_text;
    EXPECT_TRUE(t.Disassemble(binary, &output_text));
    EXPECT_EQ(input_text, output_text);
  }
  {
    std::string output_text;
    EXPECT_TRUE(t.Disassemble(binary.data(), binary.size(), &output_text));
    EXPECT_EQ(input_text, output_text);
  }
}

TEST(CppInterface, SuccessfulValidation) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  int invocation_count = 0;
  t.SetMessageConsumer([&invocation_count](spv_message_level_t, const char*,
                                           const spv_position_t&, const char*) {
    ++invocation_count;
  });

  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(Header(), &binary));
  EXPECT_TRUE(t.Validate(binary));
  EXPECT_EQ(0, invocation_count);
}

TEST(CppInterface, ValidateOverloads) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(Header(), &binary));

  { EXPECT_TRUE(t.Validate(binary)); }
  { EXPECT_TRUE(t.Validate(binary.data(), binary.size())); }
}

TEST(CppInterface, ValidateEmptyModule) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  int invocation_count = 0;
  t.SetMessageConsumer(
      [&invocation_count](spv_message_level_t level, const char* source,
                          const spv_position_t& position, const char* message) {
        ++invocation_count;
        EXPECT_EQ(SPV_MSG_ERROR, level);
        EXPECT_STREQ("input", source);
        EXPECT_EQ(0u, position.line);
        EXPECT_EQ(0u, position.column);
        EXPECT_EQ(0u, position.index);
        EXPECT_STREQ("Invalid SPIR-V magic number.", message);
      });
  EXPECT_FALSE(t.Validate({}));
  EXPECT_EQ(1, invocation_count);
}

// Returns the assembly for a SPIR-V module with a struct declaration
// with the given number of members.
std::string MakeModuleHavingStruct(int num_members) {
  std::stringstream os;
  os << Header();
  os << R"(%1 = OpTypeInt 32 0
           %2 = OpTypeStruct)";
  for (int i = 0; i < num_members; i++) os << " %1";
  return os.str();
}

TEST(CppInterface, ValidateWithOptionsPass) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary));
  const ValidatorOptions opts;

  EXPECT_TRUE(t.Validate(binary.data(), binary.size(), opts));
}

TEST(CppInterface, ValidateWithOptionsFail) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary));
  ValidatorOptions opts;
  opts.SetUniversalLimit(spv_validator_limit_max_struct_members, 9);
  std::stringstream os;
  t.SetMessageConsumer([&os](spv_message_level_t, const char*,
                             const spv_position_t&,
                             const char* message) { os << message; });

  EXPECT_FALSE(t.Validate(binary.data(), binary.size(), opts));
  EXPECT_THAT(
      os.str(),
      HasSubstr(
          "Number of OpTypeStruct members (10) has exceeded the limit (9)"));
}

// Checks that after running the given optimizer |opt| on the given |original|
// source code, we can get the given |optimized| source code.
void CheckOptimization(const std::string& original,
                       const std::string& optimized, const Optimizer& opt) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> original_binary;
  ASSERT_TRUE(t.Assemble(original, &original_binary));

  std::vector<uint32_t> optimized_binary;
  EXPECT_TRUE(opt.Run(original_binary.data(), original_binary.size(),
                      &optimized_binary));

  std::string optimized_text;
  EXPECT_TRUE(t.Disassemble(optimized_binary, &optimized_text));
  EXPECT_EQ(optimized, optimized_text);
}

TEST(CppInterface, OptimizeEmptyModule) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  EXPECT_TRUE(t.Assemble("", &binary));

  Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  o.RegisterPass(CreateStripDebugInfoPass());

  // Fails to validate.
  EXPECT_FALSE(o.Run(binary.data(), binary.size(), &binary));
}

TEST(CppInterface, OptimizeModifiedModule) {
  Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  o.RegisterPass(CreateStripDebugInfoPass());
  CheckOptimization(Header() + "OpSource GLSL 450", Header(), o);
}

TEST(CppInterface, OptimizeMulitplePasses) {
  std::string original_text = Header() +
                              "OpSource GLSL 450 "
                              "OpDecorate %true SpecId 1 "
                              "%bool = OpTypeBool "
                              "%true = OpSpecConstantTrue %bool";

  Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  o.RegisterPass(CreateStripDebugInfoPass())
      .RegisterPass(CreateFreezeSpecConstantValuePass());

  std::string expected_text = Header() +
                              "%bool = OpTypeBool\n"
                              "%true = OpConstantTrue %bool\n";

  CheckOptimization(original_text, expected_text, o);
}

TEST(CppInterface, OptimizeDoNothingWithPassToken) {
  CreateFreezeSpecConstantValuePass();
  auto token = CreateUnifyConstantPass();
}

TEST(CppInterface, OptimizeReassignPassToken) {
  auto token = CreateNullPass();
  token = CreateStripDebugInfoPass();

  CheckOptimization(
      Header() + "OpSource GLSL 450", Header(),
      Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token)));
}

TEST(CppInterface, OptimizeMoveConstructPassToken) {
  auto token1 = CreateStripDebugInfoPass();
  Optimizer::PassToken token2(std::move(token1));

  CheckOptimization(
      Header() + "OpSource GLSL 450", Header(),
      Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2)));
}

TEST(CppInterface, OptimizeMoveAssignPassToken) {
  auto token1 = CreateStripDebugInfoPass();
  auto token2 = CreateNullPass();
  token2 = std::move(token1);

  CheckOptimization(
      Header() + "OpSource GLSL 450", Header(),
      Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2)));
}

TEST(CppInterface, OptimizeSameAddressForOriginalOptimizedBinary) {
  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  std::vector<uint32_t> binary;
  ASSERT_TRUE(t.Assemble(Header() + "OpSource GLSL 450", &binary));

  EXPECT_TRUE(Optimizer(SPV_ENV_UNIVERSAL_1_1)
                  .RegisterPass(CreateStripDebugInfoPass())
                  .Run(binary.data(), binary.size(), &binary));

  std::string optimized_text;
  EXPECT_TRUE(t.Disassemble(binary, &optimized_text));
  EXPECT_EQ(Header(), optimized_text);
}

// TODO(antiagainst): tests for SetMessageConsumer().

}  // namespace
}  // namespace spvtools
<|endoftext|>


<commit_before>/*
 * Copyright 2008 Google Inc.
 * Copyright 2006, 2007 Nintendo Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define VERBOSE

#include <es.h>
#include "cpu.h"
#include "io.h"
#include "mps.h"

bool Mps::ConfigurationTableHeader::
accept(Visitor& visitor)
{
    u8* ptr = reinterpret_cast<u8*>(this);
    ptr += sizeof(ConfigurationTableHeader);
    bool cont = true;
    for (int i = 0; cont && i < entryCount; ++i)
    {
        switch (*ptr)
        {
        case PROCESSOR:
            cont = visitor.at(reinterpret_cast<Processor*>(ptr));
            ptr += sizeof(Processor);
            break;
        case BUS:
            cont = visitor.at(reinterpret_cast<Bus*>(ptr));
            ptr += sizeof(Bus);
            break;
        case IO_APIC:
            cont = visitor.at(reinterpret_cast<IOApic*>(ptr));
            ptr += sizeof(IOApic);
            break;
        case IO_INTERRUPT_ASSIGNMENT:
            cont = visitor.at(reinterpret_cast<InterruptAssignment*>(ptr));
            ptr += sizeof(InterruptAssignment);
            break;
        case LOCAL_INTERRUPT_ASSIGNMENT:
            cont = visitor.at(reinterpret_cast<LocalInterruptAssignment*>(ptr));
            ptr += sizeof(LocalInterruptAssignment);
            break;
        default:
            cont = false;
            break;
        }
    }
    return true;
}

Mps::FloatingPointerStructure* Mps::
lookup(void* base, int len)
{
#ifdef VERBOSE
    esReport("Mps::lookup() at %p, %u\n", base, len);
#endif
    u8* ptr = static_cast<u8*>(base);
    while (sizeof(FloatingPointerStructure) <= len--)
    {
        if (ptr[0] == '_' && ptr[1] == 'M' && ptr[2] == 'P' && ptr[3] == '_')
        {
            u8 sum = 0;
            for (int i = 0; i < 16; i++)
            {
                sum += ptr[i];
            }
            if (sum == 0)
            {
                return reinterpret_cast<FloatingPointerStructure*>(ptr);
            }
        }
        ++ptr;
    }
    return 0;
}

Mps::FloatingPointerStructure* Mps::
lookup()
{
    FloatingPointerStructure* fps;
    u16* ebda = reinterpret_cast<u16*>(0x40e);  // for EBDA segment

    if (*ebda)
    {
        // Lookup the first kilobyte of Extended BIOS Data Area.
        fps = lookup(reinterpret_cast<void*>(*ebda * 16), 1024);
        if (fps)
        {
            return fps;
        }
    }
    else
    {
        // Lookup the last kilobyte of system base memory.
        fps = lookup(reinterpret_cast<void*>(*reinterpret_cast<u16*>(0x413) * 1024), 1024);
        if (fps)
        {
            return fps;
        }
    }

    // Lookup the BIOS ROM address space between 0F0000h and 0FFFFFh.
    fps = lookup((void*) 0xf0000, 0x10000);
    if (fps)
    {
        return fps;
    }

    return 0;
}

Mps::
Mps() :
    fps(lookup()),
    cth(0)
{
    ASSERT(sizeof(FloatingPointerStructure) == 16);
    ASSERT(sizeof(ConfigurationTableHeader) == 44);
    ASSERT(sizeof(Processor) == 20);
    ASSERT(sizeof(Bus) == 8);
    ASSERT(sizeof(IOApic) == 8);
    ASSERT(sizeof(InterruptAssignment) == 8);
    ASSERT(sizeof(LocalInterruptAssignment) == 8);

    if (fps == 0)
    {
        return;
    }
#ifdef VERBOSE
    fps->print();
#endif

    cth = reinterpret_cast<ConfigurationTableHeader*>(fps->address);
    if (!cth)
    {
        processorCount = 2;
    }
    else
    {
        cth->accept(processorCount);
#ifdef VERBOSE
        cth->print();
        Printer printer;
        cth->accept(printer);
#endif
    }
    ASSERT(0 < processorCount);
#ifdef VERBOSE
    esReport("MPS %d.%d (%d processor%s)\n",
             1 + fps->specRev / 10, fps->specRev % 10,
             static_cast<int>(processorCount),
             (1 < processorCount) ? "s" : "");
#endif

    fps = reinterpret_cast<FloatingPointerStructure*>(0x80000000 |reinterpret_cast<unsigned long>(fps));
    if (cth)
    {
        cth = reinterpret_cast<ConfigurationTableHeader*>(0x80000000 |reinterpret_cast<unsigned long>(cth));
    }
}

volatile u32* Mps::
getInterruptAssignment(unsigned int bus, unsigned int irq, InterruptAssignment& assignment)
{
    if (cth == 0)
    {
        if (16 <= irq || irq == 2)
        {
            return 0;
        }
        assignment.entryType = IO_INTERRUPT_ASSIGNMENT;
        assignment.type = 0;    // INT
        assignment.flags = 0;
        assignment.busID = 0;
        assignment.busIRQ = irq;
        assignment.apicID = 0;
        assignment.apicINTINn = (irq == 0) ? 2 : irq;
        return reinterpret_cast<u32*>(0xfec00000);
    }

    LookupAssignment entry(bus, irq);
    cth->accept(entry);
    if (!entry)
    {
        return 0;
    }

    LookupIoApic apic(entry->apicID);
    cth->accept(apic);
    if (!apic || !apic->isUsable())
    {
        return 0;
    }

    assignment = *entry;
    return reinterpret_cast<u32*>(apic->address);
}
<commit_msg>(Mps()) : Add a workaround for qemu/Fedora 11.<commit_after>/*
 * Copyright 2008 Google Inc.
 * Copyright 2006, 2007 Nintendo Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define VERBOSE

#include <es.h>
#include "cpu.h"
#include "io.h"
#include "mps.h"

bool Mps::ConfigurationTableHeader::
accept(Visitor& visitor)
{
    u8* ptr = reinterpret_cast<u8*>(this);
    ptr += sizeof(ConfigurationTableHeader);
    bool cont = true;
    for (int i = 0; cont && i < entryCount; ++i)
    {
        switch (*ptr)
        {
        case PROCESSOR:
            cont = visitor.at(reinterpret_cast<Processor*>(ptr));
            ptr += sizeof(Processor);
            break;
        case BUS:
            cont = visitor.at(reinterpret_cast<Bus*>(ptr));
            ptr += sizeof(Bus);
            break;
        case IO_APIC:
            cont = visitor.at(reinterpret_cast<IOApic*>(ptr));
            ptr += sizeof(IOApic);
            break;
        case IO_INTERRUPT_ASSIGNMENT:
            cont = visitor.at(reinterpret_cast<InterruptAssignment*>(ptr));
            ptr += sizeof(InterruptAssignment);
            break;
        case LOCAL_INTERRUPT_ASSIGNMENT:
            cont = visitor.at(reinterpret_cast<LocalInterruptAssignment*>(ptr));
            ptr += sizeof(LocalInterruptAssignment);
            break;
        default:
            cont = false;
            break;
        }
    }
    return true;
}

Mps::FloatingPointerStructure* Mps::
lookup(void* base, int len)
{
#ifdef VERBOSE
    esReport("Mps::lookup() at %p, %u\n", base, len);
#endif
    u8* ptr = static_cast<u8*>(base);
    while (sizeof(FloatingPointerStructure) <= len--)
    {
        if (ptr[0] == '_' && ptr[1] == 'M' && ptr[2] == 'P' && ptr[3] == '_')
        {
            u8 sum = 0;
            for (int i = 0; i < 16; i++)
            {
                sum += ptr[i];
            }
            if (sum == 0)
            {
                return reinterpret_cast<FloatingPointerStructure*>(ptr);
            }
        }
        ++ptr;
    }
    return 0;
}

Mps::FloatingPointerStructure* Mps::
lookup()
{
    FloatingPointerStructure* fps;
    u16* ebda = reinterpret_cast<u16*>(0x40e);  // for EBDA segment

    if (*ebda)
    {
        // Lookup the first kilobyte of Extended BIOS Data Area.
        fps = lookup(reinterpret_cast<void*>(*ebda * 16), 1024);
        if (fps)
        {
            return fps;
        }
    }
    else
    {
        // Lookup the last kilobyte of system base memory.
        fps = lookup(reinterpret_cast<void*>(*reinterpret_cast<u16*>(0x413) * 1024), 1024);
        if (fps)
        {
            return fps;
        }
    }

    // Lookup the BIOS ROM address space between 0F0000h and 0FFFFFh.
    fps = lookup((void*) 0xf0000, 0x10000);
    if (fps)
    {
        return fps;
    }

    return 0;
}

Mps::
Mps() :
    fps(lookup()),
    cth(0)
{
    ASSERT(sizeof(FloatingPointerStructure) == 16);
    ASSERT(sizeof(ConfigurationTableHeader) == 44);
    ASSERT(sizeof(Processor) == 20);
    ASSERT(sizeof(Bus) == 8);
    ASSERT(sizeof(IOApic) == 8);
    ASSERT(sizeof(InterruptAssignment) == 8);
    ASSERT(sizeof(LocalInterruptAssignment) == 8);

    if (fps == 0)
    {
        return;
    }
#ifdef VERBOSE
    fps->print();
#endif

    cth = reinterpret_cast<ConfigurationTableHeader*>(fps->address);
    if (!cth)
    {
        processorCount = 2;
    }
    else
    {
        cth->accept(processorCount);
#ifdef VERBOSE
        cth->print();
        Printer printer;
        cth->accept(printer);
#endif
    }
    ASSERT(0 < processorCount);
#ifdef VERBOSE
    esReport("MPS %d.%d (%d processor%s)\n",
             1 + fps->specRev / 10, fps->specRev % 10,
             static_cast<int>(processorCount),
             (1 < processorCount) ? "s" : "");
#endif

    // TODO: The following block is a workaround for qemu/Fedora 11
    //       so as not to use APIC. This should be removed in future.
    if (static_cast<int>(processorCount) == 1)
    {
        fps = 0;
        cth = 0;
        return;
    }

    fps = reinterpret_cast<FloatingPointerStructure*>(0x80000000 |reinterpret_cast<unsigned long>(fps));
    if (cth)
    {
        cth = reinterpret_cast<ConfigurationTableHeader*>(0x80000000 |reinterpret_cast<unsigned long>(cth));
    }
}

volatile u32* Mps::
getInterruptAssignment(unsigned int bus, unsigned int irq, InterruptAssignment& assignment)
{
    if (cth == 0)
    {
        if (16 <= irq || irq == 2)
        {
            return 0;
        }
        assignment.entryType = IO_INTERRUPT_ASSIGNMENT;
        assignment.type = 0;    // INT
        assignment.flags = 0;
        assignment.busID = 0;
        assignment.busIRQ = irq;
        assignment.apicID = 0;
        assignment.apicINTINn = (irq == 0) ? 2 : irq;
        return reinterpret_cast<u32*>(0xfec00000);
    }

    LookupAssignment entry(bus, irq);
    cth->accept(entry);
    if (!entry)
    {
        return 0;
    }

    LookupIoApic apic(entry->apicID);
    cth->accept(apic);
    if (!apic || !apic->isUsable())
    {
        return 0;
    }

    assignment = *entry;
    return reinterpret_cast<u32*>(apic->address);
}
<|endoftext|>


<commit_before>#include "generatekey.h"

bool fill_key_sigs(PGPSecretKey & private_key, const std::string & passphrase, std::string & error){
    BBS(static_cast <PGPMPI> (static_cast <uint32_t> (now()))); // seed just in case not seeded

    if (!private_key.meaningful(error)){
        error += "Error: Bad key.\n";
        return false;
    }

    const std::string keyid = private_key.keyid();
    const Tag5::Ptr primary = std::static_pointer_cast <Tag5> (private_key.get_packets()[0]);
    PGP::Packets packets = private_key.get_packets_clone();

    Key::Ptr key = nullptr;
    User::Ptr user = nullptr;

    for(Packet::Ptr & p : packets){
        if (Packet::is_primary_key(p -> get_tag())){
            key = std::static_pointer_cast <Key> (p);
            user = nullptr;
        }
        else if (Packet::is_subkey(p -> get_tag())){
            key = std::static_pointer_cast <Key> (p);
        }
        else if (Packet::is_user(p -> get_tag())){
            user = std::static_pointer_cast <User> (p);
        }
        else if (p -> get_tag() == Packet::SIGNATURE){
            Tag2::Ptr sig = std::static_pointer_cast <Tag2> (p);

            // only fill in keys that are supposed to be signed by the primary key
            // don't fill in empty key IDs
            if (sig -> get_keyid() == keyid){
                if (Signature_Type::is_certification(sig -> get_type())){
                    if (key -> get_tag() == Packet::SECRET_KEY){
                        const Tag5::Ptr tag5 = std::static_pointer_cast <Tag5> (key);
                        sig = sign_primary_key(primary, passphrase, tag5, user, sig, error);
                    }
                    else{
                        error += "Error: Certification signature attempted to be made for a non-primary key.\n";
                        return false;
                    }
                }
                else if (sig -> get_type() == Signature_Type::SUBKEY_BINDING_SIGNATURE){
                    if (key -> get_tag() == Packet::SECRET_SUBKEY){
                        const Tag7::Ptr tag7 = std::static_pointer_cast <Tag7> (key);
                        sig = sign_subkey_binding(primary, passphrase, tag7, sig, error);
                    }
                    else{
                        error += "Error: Subkey Binding signature attempted to be made for a non-subkey.\n";
                        return false;
                    }
                }
                else if (sig -> get_type() == Signature_Type::KEY_REVOCATION_SIGNATURE){
                    sig = revoke_sig(primary, passphrase, primary, sig, error);
                }
                else if (sig -> get_type() == Signature_Type::SUBKEY_REVOCATION_SIGNATURE){
                    sig = revoke_sig(primary, passphrase, key, sig, error);
                }
                else if (sig -> get_type() == Signature_Type::CERTIFICATION_REVOCATION_SIGNATURE){
                    sig = revoke_uid_sig(primary, passphrase, user, sig, error);
                }
                else{
                    std::cerr << "Warning: Bad or unhandled signature type: 0x" << makehex(sig -> get_type(), 2) << std::endl;
                }
            }
        }
        else{
            // should never come here
            error += "Error: Random packet found.\n";
            return false;
        }
    }

    private_key.set_packets(packets);

    return true;
}

PGPSecretKey generate_key(KeyGen & config, std::string & error){
    BBS(static_cast <PGPMPI> (static_cast <uint32_t> (now()))); // seed just in case not seeded

    if (!config.valid(error)){
        error += "Error: Bad key generation configuration.\n";
        return PGPSecretKey();
    }

    // collection of packets to be put into final key
    PGP::Packets packets;

    // Key creation time
    const time_t time = now();

    // generate Primary Key, User ID, and Signature packets

    // generate public key values for primary key
    PKA::Values pub;
    PKA::Values pri;
    if (!generate_keypair(config.pka, generate_pka_params(config.pka, config.bits >> 1), pri, pub)){
        error += "Error: Could not generate primary key pair.\n";
        return PGPSecretKey();
    }

    // convert the secret values into a string
    std::string secret;
    for(PGPMPI const & mpi : pri){
        secret += write_MPI(mpi);
    }

    // Secret Key Packet
    Tag5::Ptr sec = std::make_shared <Tag5> ();
    sec -> set_version(4);
    sec -> set_time(time);
    sec -> set_pka(config.pka);
    sec -> set_mpi(pub);
    sec -> set_s2k_con(0); // no passphrase up to here

    // encrypt secret only if there is a passphrase
    if (config.passphrase.size()){
        sec -> set_s2k_con(254);
        sec -> set_sym(config.sym);

        // Secret Key Packet S2K
        S2K3::Ptr s2k3 = std::make_shared <S2K3> ();
        s2k3 -> set_hash(config.hash);
        s2k3 -> set_salt(unhexlify(bintohex(BBS().rand(64))));
        s2k3 -> set_count(96);

        // calculate the key from the passphrase
        const std::string session_key = s2k3 -> run(config.passphrase, Sym::KEY_LENGTH.at(config.sym) >> 3);

        // add checksum to secret
        secret += use_hash(Hash::SHA1, secret);

        // encrypt private key value
        sec -> set_s2k(s2k3);
        sec -> set_IV(unhexlify(bintohex(BBS().rand(Sym::BLOCK_LENGTH.at(config.sym)))));
        secret = use_normal_CFB_encrypt(config.sym, secret + use_hash(Hash::SHA1, secret), session_key, sec -> get_IV());
    }
    else{
        // add checksum to secret
        uint16_t checksum = 0;
        for(uint8_t const c : secret){
            checksum += static_cast <uint16_t> (c);
        }

        secret += unhexlify(makehex(checksum, 4));
    }

    sec -> set_secret(secret);

    // first packet is primary key
    packets.push_back(sec);

    // get ID of primary key
    const std::string keyid = sec -> get_keyid();

    // generate User ID and Signature packets
    for(KeyGen::UserID const & id : config.uids){
        // User ID
        Tag13::Ptr uid = std::make_shared <Tag13> ();
        uid -> set_contents(id.user, id.comment, id.email);

        packets.push_back(uid);

        Tag2::Ptr sig = std::make_shared <Tag2> ();
        sig -> set_version(4);
        sig -> set_type(Signature_Type::POSITIVE_CERTIFICATION_OF_A_USER_ID_AND_PUBLIC_KEY_PACKET);
        sig -> set_pka(config.pka);
        sig -> set_hash(id.sig);

        // set creation time
        Tag2Sub2::Ptr tag2sub2 = std::make_shared <Tag2Sub2> ();
        tag2sub2 -> set_time(time);
        sig -> set_hashed_subpackets({tag2sub2});

        // set issuer
        Tag2Sub16::Ptr tag2sub16 = std::make_shared <Tag2Sub16> ();
        tag2sub16 -> set_keyid(keyid);
        sig -> set_unhashed_subpackets({tag2sub16});

        // sign Primary Key and User ID
        sig = sign_primary_key(sec, config.passphrase, sec, uid, sig, error);
        if (!sig){
            error += "Error: Failed to sign primary config.\n";
            return PGPSecretKey();
        }

        packets.push_back(sig);
    }

    // generate 0 or more subkeys and associated signature packet
    for(KeyGen::SubkeyGen const & skey : config.subkeys){
        PKA::Values subkey_pub;
        PKA::Values subkey_pri;
        if (!generate_keypair(skey.pka, generate_pka_params(skey.pka, skey.bits >> 1), subkey_pri, subkey_pub)){
            error += "Error: Could not generate subkey pair.\n";
            return PGPSecretKey();
        }

        // convert the secret values into a string
        secret = "";
        for(PGPMPI const & mpi : subkey_pri){
            secret += write_MPI(mpi);
        }

        // Secret Subkey Packet
        Tag7::Ptr subkey = std::make_shared <Tag7> ();
        subkey -> set_version(4);
        subkey -> set_time(time);
        subkey -> set_pka(skey.pka);
        subkey -> set_mpi(subkey_pub);
        subkey -> set_s2k_con(0); // no passphrase up to here

        // encrypt secret only if there is a passphrase
        if (config.passphrase.size()){
            subkey -> set_s2k_con(254);
            subkey -> set_sym(skey.sym);

            // Secret Subkey S2K
            S2K3::Ptr s2k3 = std::make_shared <S2K3> ();
            s2k3 -> set_hash(skey.hash);
            s2k3 -> set_salt(unhexlify(bintohex(BBS().rand(64)))); // new salt value
            s2k3 -> set_count(96);

            // calculate the key from the passphrase
            std::string session_key = s2k3 -> run(config.passphrase, Sym::KEY_LENGTH.at(skey.sym) >> 3);

            // add checksum to secret
            secret += use_hash(Hash::SHA1, secret);

            // encrypt private key value
            subkey -> set_s2k(s2k3);
            subkey -> set_IV(unhexlify(bintohex(BBS().rand(Sym::BLOCK_LENGTH.at(skey.sym)))));
            secret = use_normal_CFB_encrypt(skey.sym, secret + use_hash(Hash::SHA1, secret), session_key, subkey -> get_IV());
        }
        else{
            // add checksum to secret
            uint16_t checksum = 0;
            for(uint8_t const c : secret){
                checksum += c;
            }

            secret += unhexlify(makehex(checksum, 4));
        }

        subkey -> set_secret(secret);

        packets.push_back(subkey);

        // Subkey Binding Signature
        Tag2::Ptr subsig = std::make_shared <Tag2> ();
        subsig -> set_version(4);
        subsig -> set_type(Signature_Type::SUBKEY_BINDING_SIGNATURE);
        subsig -> set_pka(config.pka);
        subsig -> set_hash(skey.sig);

        // set creation time
        Tag2Sub2::Ptr tag2sub2 = std::make_shared <Tag2Sub2> ();
        tag2sub2 -> set_time(time);
        subsig -> set_hashed_subpackets({tag2sub2});

        // set issuer
        Tag2Sub16::Ptr tag2sub16 = std::make_shared <Tag2Sub16> ();
        tag2sub16 -> set_keyid(keyid);
        subsig -> set_unhashed_subpackets({tag2sub16});

        // sign subkey
        subsig = sign_subkey_binding(sec, config.passphrase, subkey, subsig, error);
        if (!subsig){
            error += "Error: Subkey signing failure.\n";
            return PGPSecretKey();
        }

        packets.push_back(subsig);
    }

    // put everything into a private key
    PGPSecretKey private_key;
    private_key.set_keys({std::make_pair("Version", "cc")});
    private_key.set_packets(packets);
    private_key.set_armored(true);

    // can call fill_key_sigs as well
    // return fill_key_sigs(private_key, config.passphrase, error);
    return private_key;
}
<commit_msg>Small fix for generatekey.cpp<commit_after>#include "generatekey.h"

bool fill_key_sigs(PGPSecretKey & private_key, const std::string & passphrase, std::string & error){
    BBS(static_cast <PGPMPI> (static_cast <uint32_t> (now()))); // seed just in case not seeded

    if (!private_key.meaningful(error)){
        error += "Error: Bad key.\n";
        return false;
    }

    const std::string keyid = private_key.keyid();
    const Tag5::Ptr primary = std::static_pointer_cast <Tag5> (private_key.get_packets()[0]);
    PGP::Packets packets = private_key.get_packets_clone();

    Key::Ptr key = nullptr;
    User::Ptr user = nullptr;

    for(Packet::Ptr & p : packets){
        if (Packet::is_key_packet(p -> get_tag())){
            key = std::static_pointer_cast <Key> (p);
            user = nullptr;
        }
        else if (Packet::is_user(p -> get_tag())){
            user = std::static_pointer_cast <User> (p);
        }
        else if (p -> get_tag() == Packet::SIGNATURE){
            Tag2::Ptr sig = std::static_pointer_cast <Tag2> (p);

            // only fill in keys that are supposed to be signed by the primary key
            // don't fill in empty key IDs
            if (sig -> get_keyid() == keyid){
                if (Signature_Type::is_certification(sig -> get_type())){
                    if (key -> get_tag() == Packet::SECRET_KEY){
                        const Tag5::Ptr tag5 = std::static_pointer_cast <Tag5> (key);
                        sig = sign_primary_key(primary, passphrase, tag5, user, sig, error);
                    }
                    else{
                        error += "Error: Certification signature attempted to be made for a non-primary key.\n";
                        return false;
                    }
                }
                else if (sig -> get_type() == Signature_Type::SUBKEY_BINDING_SIGNATURE){
                    if (key -> get_tag() == Packet::SECRET_SUBKEY){
                        const Tag7::Ptr tag7 = std::static_pointer_cast <Tag7> (key);
                        sig = sign_subkey_binding(primary, passphrase, tag7, sig, error);
                    }
                    else{
                        error += "Error: Subkey Binding signature attempted to be made for a non-subkey.\n";
                        return false;
                    }
                }
                else if (sig -> get_type() == Signature_Type::KEY_REVOCATION_SIGNATURE){
                    sig = revoke_sig(primary, passphrase, primary, sig, error);
                }
                else if (sig -> get_type() == Signature_Type::SUBKEY_REVOCATION_SIGNATURE){
                    sig = revoke_sig(primary, passphrase, key, sig, error);
                }
                else if (sig -> get_type() == Signature_Type::CERTIFICATION_REVOCATION_SIGNATURE){
                    sig = revoke_uid_sig(primary, passphrase, user, sig, error);
                }
                else{
                    std::cerr << "Warning: Bad or unhandled signature type: 0x" << makehex(sig -> get_type(), 2) << std::endl;
                }
            }
        }
        else{
            // should never come here
            error += "Error: Random packet found.\n";
            return false;
        }
    }

    private_key.set_packets(packets);

    return true;
}

PGPSecretKey generate_key(KeyGen & config, std::string & error){
    BBS(static_cast <PGPMPI> (static_cast <uint32_t> (now()))); // seed just in case not seeded

    if (!config.valid(error)){
        error += "Error: Bad key generation configuration.\n";
        return PGPSecretKey();
    }

    // collection of packets to be put into final key
    PGP::Packets packets;

    // Key creation time
    const time_t time = now();

    // generate Primary Key, User ID, and Signature packets

    // generate public key values for primary key
    PKA::Values pub;
    PKA::Values pri;
    if (!generate_keypair(config.pka, generate_pka_params(config.pka, config.bits >> 1), pri, pub)){
        error += "Error: Could not generate primary key pair.\n";
        return PGPSecretKey();
    }

    // convert the secret values into a string
    std::string secret;
    for(PGPMPI const & mpi : pri){
        secret += write_MPI(mpi);
    }

    // Secret Key Packet
    Tag5::Ptr sec = std::make_shared <Tag5> ();
    sec -> set_version(4);
    sec -> set_time(time);
    sec -> set_pka(config.pka);
    sec -> set_mpi(pub);
    sec -> set_s2k_con(0); // no passphrase up to here

    // encrypt secret only if there is a passphrase
    if (config.passphrase.size()){
        sec -> set_s2k_con(254);
        sec -> set_sym(config.sym);

        // Secret Key Packet S2K
        S2K3::Ptr s2k3 = std::make_shared <S2K3> ();
        s2k3 -> set_hash(config.hash);
        s2k3 -> set_salt(unhexlify(bintohex(BBS().rand(64))));
        s2k3 -> set_count(96);

        // calculate the key from the passphrase
        const std::string session_key = s2k3 -> run(config.passphrase, Sym::KEY_LENGTH.at(config.sym) >> 3);

        // add checksum to secret
        secret += use_hash(Hash::SHA1, secret);

        // encrypt private key value
        sec -> set_s2k(s2k3);
        sec -> set_IV(unhexlify(bintohex(BBS().rand(Sym::BLOCK_LENGTH.at(config.sym)))));
        secret = use_normal_CFB_encrypt(config.sym, secret, session_key, sec -> get_IV());
    }
    else{
        // add checksum to secret
        uint16_t checksum = 0;
        for(uint8_t const c : secret){
            checksum += static_cast <uint16_t> (c);
        }

        secret += unhexlify(makehex(checksum, 4));
    }

    sec -> set_secret(secret);

    // first packet is primary key
    packets.push_back(sec);

    // get ID of primary key
    const std::string keyid = sec -> get_keyid();

    // generate User ID and Signature packets
    for(KeyGen::UserID const & id : config.uids){
        // User ID
        Tag13::Ptr uid = std::make_shared <Tag13> ();
        uid -> set_contents(id.user, id.comment, id.email);

        packets.push_back(uid);

        Tag2::Ptr sig = std::make_shared <Tag2> ();
        sig -> set_version(4);
        sig -> set_type(Signature_Type::POSITIVE_CERTIFICATION_OF_A_USER_ID_AND_PUBLIC_KEY_PACKET);
        sig -> set_pka(config.pka);
        sig -> set_hash(id.sig);

        // set creation time
        Tag2Sub2::Ptr tag2sub2 = std::make_shared <Tag2Sub2> ();
        tag2sub2 -> set_time(time);
        sig -> set_hashed_subpackets({tag2sub2});

        // set issuer
        Tag2Sub16::Ptr tag2sub16 = std::make_shared <Tag2Sub16> ();
        tag2sub16 -> set_keyid(keyid);
        sig -> set_unhashed_subpackets({tag2sub16});

        // sign Primary Key and User ID
        sig = sign_primary_key(sec, config.passphrase, sec, uid, sig, error);
        if (!sig){
            error += "Error: Failed to sign primary config.\n";
            return PGPSecretKey();
        }

        packets.push_back(sig);
    }
/*
    // generate 0 or more subkeys and associated signature packet
    for(KeyGen::SubkeyGen const & skey : config.subkeys){
        PKA::Values subkey_pub;
        PKA::Values subkey_pri;
        if (!generate_keypair(skey.pka, generate_pka_params(skey.pka, skey.bits >> 1), subkey_pri, subkey_pub)){
            error += "Error: Could not generate subkey pair.\n";
            return PGPSecretKey();
        }

        // convert the secret values into a string
        secret = "";
        for(PGPMPI const & mpi : subkey_pri){
            secret += write_MPI(mpi);
        }

        // Secret Subkey Packet
        Tag7::Ptr subkey = std::make_shared <Tag7> ();
        subkey -> set_version(4);
        subkey -> set_time(time);
        subkey -> set_pka(skey.pka);
        subkey -> set_mpi(subkey_pub);
        subkey -> set_s2k_con(0); // no passphrase up to here

        // encrypt secret only if there is a passphrase
        if (config.passphrase.size()){
            subkey -> set_s2k_con(254);
            subkey -> set_sym(skey.sym);

            // Secret Subkey S2K
            S2K3::Ptr s2k3 = std::make_shared <S2K3> ();
            s2k3 -> set_hash(skey.hash);
            s2k3 -> set_salt(unhexlify(bintohex(BBS().rand(64)))); // new salt value
            s2k3 -> set_count(96);

            // calculate the key from the passphrase
            std::string session_key = s2k3 -> run(config.passphrase, Sym::KEY_LENGTH.at(skey.sym) >> 3);

            // add checksum to secret
            secret += use_hash(Hash::SHA1, secret);

            // encrypt private key value
            subkey -> set_s2k(s2k3);
            subkey -> set_IV(unhexlify(bintohex(BBS().rand(Sym::BLOCK_LENGTH.at(skey.sym)))));
            secret = use_normal_CFB_encrypt(skey.sym, secret + use_hash(Hash::SHA1, secret), session_key, subkey -> get_IV());
        }
        else{
            // add checksum to secret
            uint16_t checksum = 0;
            for(uint8_t const c : secret){
                checksum += c;
            }

            secret += unhexlify(makehex(checksum, 4));
        }

        subkey -> set_secret(secret);

        packets.push_back(subkey);

        // Subkey Binding Signature
        Tag2::Ptr subsig = std::make_shared <Tag2> ();
        subsig -> set_version(4);
        subsig -> set_type(Signature_Type::SUBKEY_BINDING_SIGNATURE);
        subsig -> set_pka(config.pka);
        subsig -> set_hash(skey.sig);

        // set creation time
        Tag2Sub2::Ptr tag2sub2 = std::make_shared <Tag2Sub2> ();
        tag2sub2 -> set_time(time);
        subsig -> set_hashed_subpackets({tag2sub2});

        // set issuer
        Tag2Sub16::Ptr tag2sub16 = std::make_shared <Tag2Sub16> ();
        tag2sub16 -> set_keyid(keyid);
        subsig -> set_unhashed_subpackets({tag2sub16});

        // sign subkey
        subsig = sign_subkey_binding(sec, config.passphrase, subkey, subsig, error);
        if (!subsig){
            error += "Error: Subkey signing failure.\n";
            return PGPSecretKey();
        }

        packets.push_back(subsig);
    }
*/
    // put everything into a private key
    PGPSecretKey private_key;
    private_key.set_keys({std::make_pair("Version", "cc")});
    private_key.set_packets(packets);
    private_key.set_armored(true);

    // can call fill_key_sigs as well
    // return fill_key_sigs(private_key, config.passphrase, error);
    return private_key;
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Elviss Strazdins
// This file is part of the Ouzel engine.

#include "Color.h"
#include "Vector3.h"
#include "Vector4.h"
#include "Utils.h"

namespace ouzel
{
    Color::Color(const std::string& color)
    {
        if (!color.empty())
        {
            uint32_t intValue = 0;

            if (color[0] == '#')
            {
                std::string newValue = "0x";
                newValue.append(color.begin() + 1, color.end());
                intValue = static_cast<uint32_t>(stringToLong(newValue));
            }
            else
            {
                intValue = static_cast<uint32_t>(stringToLong(color));
            }

            v[0] = static_cast<uint8_t>((intValue & 0xFF000000) >> 24);
            v[1] = static_cast<uint8_t>((intValue & 0x00FF0000) >> 16);
            v[2] = static_cast<uint8_t>((intValue & 0x0000FF00) >> 8);
            v[3] = static_cast<uint8_t>(intValue & 0x000000FF);
        }
    }

    Color& Color::operator=(const std::string& color)
    {
        if (!color.empty())
        {
            uint32_t intValue = 0;

            if (color[0] == '#')
            {
                std::string newValue = "0x";
                newValue.append(color.begin() + 1, color.end());
                intValue = static_cast<uint32_t>(stringToLong(newValue));
            }
            else
            {
                intValue = static_cast<uint32_t>(stringToLong(color));
            }

            v[0] = static_cast<uint8_t>((intValue & 0xFF000000) >> 24);
            v[1] = static_cast<uint8_t>((intValue & 0x00FF0000) >> 16);
            v[2] = static_cast<uint8_t>((intValue & 0x0000FF00) >> 8);
            v[3] = static_cast<uint8_t>(intValue & 0x000000FF);
        }

        return *this;
    }

    Color::Color(const Vector3& vec)
    {
        v[0] = static_cast<uint8_t>(vec.v[0] * 255.0f);
        v[1] = static_cast<uint8_t>(vec.v[1] * 255.0f);
        v[2] = static_cast<uint8_t>(vec.v[2] * 255.0f);
        v[3] = 0;
    }

    Color& Color::operator=(const Vector3& vec)
    {
        v[0] = static_cast<uint8_t>(vec.v[0] * 255.0f);
        v[1] = static_cast<uint8_t>(vec.v[1] * 255.0f);
        v[2] = static_cast<uint8_t>(vec.v[2] * 255.0f);
        v[3] = 0;

        return *this;
    }

    Color::Color(const Vector4& vec)
    {
        v[0] = static_cast<uint8_t>(vec.v[0] * 255.0f);
        v[1] = static_cast<uint8_t>(vec.v[1] * 255.0f);
        v[2] = static_cast<uint8_t>(vec.v[2] * 255.0f);
        v[3] = static_cast<uint8_t>(vec.v[3] * 255.0f);
    }

    Color& Color::operator=(const Vector4& vec)
    {
        v[0] = static_cast<uint8_t>(vec.v[0] * 255.0f);
        v[1] = static_cast<uint8_t>(vec.v[1] * 255.0f);
        v[2] = static_cast<uint8_t>(vec.v[2] * 255.0f);
        v[3] = static_cast<uint8_t>(vec.v[3] * 255.0f);

        return *this;
    }
} // namespace ouzel
<commit_msg>Fix the path of Utils.h<commit_after>// Copyright (C) 2017 Elviss Strazdins
// This file is part of the Ouzel engine.

#include "Color.h"
#include "Vector3.h"
#include "Vector4.h"
#include "utils/Utils.h"

namespace ouzel
{
    Color::Color(const std::string& color)
    {
        if (!color.empty())
        {
            uint32_t intValue = 0;

            if (color[0] == '#')
            {
                std::string newValue = "0x";
                newValue.append(color.begin() + 1, color.end());
                intValue = static_cast<uint32_t>(stringToLong(newValue));
            }
            else
            {
                intValue = static_cast<uint32_t>(stringToLong(color));
            }

            v[0] = static_cast<uint8_t>((intValue & 0xFF000000) >> 24);
            v[1] = static_cast<uint8_t>((intValue & 0x00FF0000) >> 16);
            v[2] = static_cast<uint8_t>((intValue & 0x0000FF00) >> 8);
            v[3] = static_cast<uint8_t>(intValue & 0x000000FF);
        }
    }

    Color& Color::operator=(const std::string& color)
    {
        if (!color.empty())
        {
            uint32_t intValue = 0;

            if (color[0] == '#')
            {
                std::string newValue = "0x";
                newValue.append(color.begin() + 1, color.end());
                intValue = static_cast<uint32_t>(stringToLong(newValue));
            }
            else
            {
                intValue = static_cast<uint32_t>(stringToLong(color));
            }

            v[0] = static_cast<uint8_t>((intValue & 0xFF000000) >> 24);
            v[1] = static_cast<uint8_t>((intValue & 0x00FF0000) >> 16);
            v[2] = static_cast<uint8_t>((intValue & 0x0000FF00) >> 8);
            v[3] = static_cast<uint8_t>(intValue & 0x000000FF);
        }

        return *this;
    }

    Color::Color(const Vector3& vec)
    {
        v[0] = static_cast<uint8_t>(vec.v[0] * 255.0f);
        v[1] = static_cast<uint8_t>(vec.v[1] * 255.0f);
        v[2] = static_cast<uint8_t>(vec.v[2] * 255.0f);
        v[3] = 0;
    }

    Color& Color::operator=(const Vector3& vec)
    {
        v[0] = static_cast<uint8_t>(vec.v[0] * 255.0f);
        v[1] = static_cast<uint8_t>(vec.v[1] * 255.0f);
        v[2] = static_cast<uint8_t>(vec.v[2] * 255.0f);
        v[3] = 0;

        return *this;
    }

    Color::Color(const Vector4& vec)
    {
        v[0] = static_cast<uint8_t>(vec.v[0] * 255.0f);
        v[1] = static_cast<uint8_t>(vec.v[1] * 255.0f);
        v[2] = static_cast<uint8_t>(vec.v[2] * 255.0f);
        v[3] = static_cast<uint8_t>(vec.v[3] * 255.0f);
    }

    Color& Color::operator=(const Vector4& vec)
    {
        v[0] = static_cast<uint8_t>(vec.v[0] * 255.0f);
        v[1] = static_cast<uint8_t>(vec.v[1] * 255.0f);
        v[2] = static_cast<uint8_t>(vec.v[2] * 255.0f);
        v[3] = static_cast<uint8_t>(vec.v[3] * 255.0f);

        return *this;
    }
} // namespace ouzel
<|endoftext|>


<commit_before>//
//  Configurable.h
//  Clock Signal
//
//  Created by Thomas Harte on 17/11/2017.
//  Copyright 2017 Thomas Harte. All rights reserved.
//

#ifndef Configurable_h
#define Configurable_h

#include "../Reflection/Struct.h"

namespace Configurable {

/*!
	A Configurable::Device provides a reflective struct listing the available runtime options for this machine.
	It can provide it populated with 'accurate' options, 'user-friendly' options or just whatever the user
	currently has selected.

	'Accurate' options should correspond to the way that this device was usually used during its lifespan.
	E.g. a ColecoVision might accurately be given composite output.

	'User-friendly' options should be more like those that a user today might most expect from an emulator.
	E.g. the ColecoVision might bump itself up to S-Video output.
*/
struct Device {
	/// Sets the current options.
	virtual void set_options(const std::unique_ptr<Reflection::Struct> &options) = 0;

	enum class OptionsType {
		Current,
		Accurate,
		UserFriendly
	};

	/// @returns Options of type @c type.
	virtual std::unique_ptr<Reflection::Struct> get_options(OptionsType type) = 0;
};

}

#endif /* Configurable_h */
<commit_msg>Adds necessary include for unique_ptr.<commit_after>//
//  Configurable.h
//  Clock Signal
//
//  Created by Thomas Harte on 17/11/2017.
//  Copyright 2017 Thomas Harte. All rights reserved.
//

#ifndef Configurable_h
#define Configurable_h

#include "../Reflection/Struct.h"

#include <memory>

namespace Configurable {

/*!
	A Configurable::Device provides a reflective struct listing the available runtime options for this machine.
	It can provide it populated with 'accurate' options, 'user-friendly' options or just whatever the user
	currently has selected.

	'Accurate' options should correspond to the way that this device was usually used during its lifespan.
	E.g. a ColecoVision might accurately be given composite output.

	'User-friendly' options should be more like those that a user today might most expect from an emulator.
	E.g. the ColecoVision might bump itself up to S-Video output.
*/
struct Device {
	/// Sets the current options.
	virtual void set_options(const std::unique_ptr<Reflection::Struct> &options) = 0;

	enum class OptionsType {
		Current,
		Accurate,
		UserFriendly
	};

	/// @returns Options of type @c type.
	virtual std::unique_ptr<Reflection::Struct> get_options(OptionsType type) = 0;
};

}

#endif /* Configurable_h */
<|endoftext|>