<commit_before>/*
 *      This node is used to perform inference in a Bayesian network.
 *      It's subscribed to interaction_monitor /BN_vars topic
 *      to obtain data and then performs inference with a BN using SMILE libraries
 *
 *      At the end it prints some statistics with the performance.
 *
 *      Lunds tekniska högskola | LTH 2015
 *      Felip Marti Carrillo
 *
 *      MIT License (MIT)
 *      Copyright (c) 2015 Felip Marti Carrillo
 */

#include "interaction_recognition_node.h"

InteractionRecognition::InteractionRecognition (void)
{

    // Init Subscriber
    this->sub = this->n.subscribe("BN_vars", 1,
                        &InteractionRecognition::perform_inference_callback, this);

    // Init Stats
    for (int i=0; i<9; i++) {
         Stats_Results[i]=0;
    }

    // Max difference to consider 2 objects different
    MAX_DIFF = 0.1;

    // Vector of strings with fails
    statsFails.resize(0);

}

InteractionRecognition::~InteractionRecognition (void)
{
}

void InteractionRecognition::perform_inference_callback
                        (const interaction_monitor::BayesianNetworkVariable& msg)
{

    if (msg.category==-1) {

        print_statistics();
        exit(0);

    }

    /// Performing Inference
    ROS_INFO("[Interaction_Recognition] Performing Inference");

    // Vars to get the handle of node
    int category = theNet.FindNode("category");
    int lastCommand = theNet.FindNode("lastCommand");
    int usrAnnounce = theNet.FindNode("usrAnnounce");
    int usrGesture = theNet.FindNode("usrGesture");
    int headingAdj = theNet.FindNode("headingAdj");
    int distanceAdj = theNet.FindNode("distanceAdj");

    // Vars to store data
    int lastCommandData=msg.last_cmd;
    int usrAnnounceData=msg.announce;
    int usrGestureData=msg.gesture;
    int headingAdjData=msg.head_adj;
    int distanceAdjData=msg.dist_adj;
    int categoryData=msg.category;

    // Setting evidence
    theNet.GetNode(lastCommand)->Value()->SetEvidence(lastCommandData);
    theNet.GetNode(usrAnnounce)->Value()->SetEvidence(usrAnnounceData);
    theNet.GetNode(usrGesture)->Value()->SetEvidence(usrGestureData);
    theNet.GetNode(headingAdj)->Value()->SetEvidence(headingAdjData);
    theNet.GetNode(distanceAdj)->Value()->SetEvidence(distanceAdjData);

    // Update the network
    theNet.UpdateBeliefs();

    // Get the result values
    DSL_sysCoordinates theCoordinates(*theNet.GetNode(category)->Value());
    DSL_idArray *theNames;
    theNames = theNet.GetNode(category)->Definition()->GetOutcomesNames();

    int objectIndex = theNames->FindPosition("object");
    int regionIndex = theNames->FindPosition("region");
    int workspaceIndex = theNames->FindPosition("workspace");
    int unknownIndex = theNames->FindPosition("unknown");

    // Probability of category
    double P_CategoryIs[4];
    // 0 P_CategoryIsObject
    // 1 P_CategoryIsRegion
    // 2 P_CategoryIsWorkspace
    // 3 P_CategoryIsUnknown

    theCoordinates[0] = objectIndex;
    theCoordinates.GoToCurrentPosition();
    // get P("category" = object)
    P_CategoryIs[0] = theCoordinates.UncheckedValue();
    ROS_INFO("[Interaction_Recognition] P(\"category\" = object) = %f",P_CategoryIs[0]);

    theCoordinates[0] = regionIndex;
    theCoordinates.GoToCurrentPosition();
    // get P("category" = region)
    P_CategoryIs[1] = theCoordinates.UncheckedValue();
    ROS_INFO("[Interaction_Recognition] P(\"category\" = region) = %f",P_CategoryIs[1]);

    theCoordinates[0] = workspaceIndex;
    theCoordinates.GoToCurrentPosition();
    // get P("category" = workspace)
    P_CategoryIs[2] = theCoordinates.UncheckedValue();
    ROS_INFO("[Interaction_Recognition] P(\"category\" = workspace) = %f",P_CategoryIs[2]);

    theCoordinates[0] = unknownIndex;
    theCoordinates.GoToCurrentPosition();
    // get P("category" = unknown)
    P_CategoryIs[3] = theCoordinates.UncheckedValue();
    ROS_INFO("[Interaction_Recognition] P(\"category\" = unknown) = %f",P_CategoryIs[3]);

    ROS_INFO("[Interaction_Recognition] User was presenting category %d", categoryData);

    /// UPDATING STATISTICS
    int CategoryWin=-1;
    double P_CategoryWin=-1;
    for (int i=0; i<4; i++) {
        if (P_CategoryWin < P_CategoryIs[i]) {
            P_CategoryWin = P_CategoryIs[i];
            CategoryWin=i;
        }
    }

    // Difference
    P_CategoryIs[0] = P_CategoryWin-P_CategoryIs[0];
    P_CategoryIs[1] = P_CategoryWin-P_CategoryIs[1];
    P_CategoryIs[2] = P_CategoryWin-P_CategoryIs[2];
    P_CategoryIs[3] = P_CategoryWin-P_CategoryIs[3];

    // Checking results category
    int equalCategory=0;
    std::vector<int> equalCategories;
    for (int i=0; i<4; i++) {
        if (P_CategoryIs[i]<MAX_DIFF) {
            equalCategory++;
            equalCategories.push_back(i);
        }
    }

    // Updating stats
    if (equalCategory==1 and categoryData == CategoryWin) {
        Stats_Results[0]++;     // Good Job!
    }
    else if (equalCategory==1 and categoryData != CategoryWin) {
        if (categoryData == 3) {
            Stats_Results[5]++;     // Nice, Unknown category classified!
        }
        else {
            Stats_Results[1]++;     // Missmatch!!
            std::ostringstream stringStream;
            stringStream << categoryData <<"->"
                         << CategoryWin;
            statsFails.push_back(stringStream.str());
        }
    }
    else if (equalCategory==2) {    // 2 are similar
        if ( P_CategoryIs[categoryData] < MAX_DIFF or categoryData == 3) {
            Stats_Results[2]++;     // Category is among them, or is Unknown
            std::ostringstream stringStream;
            stringStream << categoryData <<" -> "<< equalCategories[0]<<" or "
                         << equalCategories[1];
            statsBetween2.push_back(stringStream.str());
        }
        else {
            Stats_Results[6]++;     // Category NOT among them, FAIL
        }
    }
    else if (equalCategory==3) {    // 3 are similar
        if ( P_CategoryIs[categoryData] < MAX_DIFF or categoryData == 3) {
            Stats_Results[3]++;     // Category is among them, or is Unknown
            std::ostringstream stringStream;
            stringStream << categoryData <<" "<< equalCategories[0]<<", "
                         << equalCategories[1]<<" or " << equalCategories[2];
            statsAmong3.push_back(stringStream.str());
        }
        else {
            Stats_Results[7]++;     // Category NOT among them, FAIL
        }
    }
    else if (equalCategory==4) {    // all are similar
        Stats_Results[4]++;
    }
    else {
        Stats_Results[8]++;         //WTF??
    }

    // Clear the evidence in nodes
    theNet.GetNode(lastCommand)->Value()->ClearEvidence();
    theNet.GetNode(usrAnnounce)->Value()->ClearEvidence();
    theNet.GetNode(usrGesture)->Value()->ClearEvidence();
    theNet.GetNode(headingAdj)->Value()->ClearEvidence();
    theNet.GetNode(distanceAdj)->Value()->ClearEvidence();

    ROS_INFO("[Interaction_Recognition] * * * * ") ;

}

int InteractionRecognition::Main (const char* path) {

    // Open BN GENIE file
    if (theNet.ReadFile(path) != 0) {
        ROS_ERROR("[interaction_recognition] Cannot open the Bayesian Network");
        ROS_ERROR("[interaction_recognition] BN=\"%s\"",path);
        return 1;
    }
    else {
        ROS_INFO("[interaction_recognition] Bayesian Network opened successfully");
        ROS_INFO("[interaction_recognition] BN=\"%s\"",path);
    }

    // Wait for callbacks
    ros::spin();

}

void InteractionRecognition::print_statistics()
{

    ROS_WARN("[Interaction_Learner] No more data!");
    ROS_WARN("[Interaction_Learner] So, the node will be stopped gently :)");
    ROS_INFO("[Interaction_Learner] ********** STATISTICS **********");
    ROS_INFO("[Interaction_Learner] %d are OK!!", Stats_Results[0]);
    ROS_INFO("[Interaction_Learner] %d Mismatches!!", Stats_Results[1]);
    for (int i=0; i<statsFails.size(); i++) {
        std::cout<<statsFails[i]<<"; ";
    }
    std::cout<<std::endl;
    ROS_INFO("[Interaction_Learner] %d are Similar between 2", Stats_Results[2]);
    for (int i=0; i<statsBetween2.size(); i++) {
        std::cout<<statsBetween2[i]<<"; ";
    }
    std::cout<<std::endl;
    ROS_INFO("[Interaction_Learner] %d are Similar among 3", Stats_Results[3]);
    for (int i=0; i<statsAmong3.size(); i++) {
        std::cout<<statsAmong3[i]<<"; ";
    }
    std::cout<<std::endl;
    ROS_INFO("[Interaction_Learner] %d are Similar among 4", Stats_Results[4]);
    ROS_INFO("[Interaction_Learner] %d are Unknown classified", Stats_Results[5]);
    ROS_INFO("[Interaction_Learner] %d are Similar between 2, but FAIL", Stats_Results[6]);
    ROS_INFO("[Interaction_Learner] %d are Similar among 3, but FAIL", Stats_Results[7]);
    ROS_INFO("[Interaction_Learner] %d, if > 0, something weird is going on, CHECK CODE!!!",
                Stats_Results[8]);
    ROS_INFO("[Interaction_Learner] ********** ********** **********");

}

int main(int argc, char **argv)
{

    ros::init(argc, argv, "interaction_recognition");
    if (argc != 2) {
        ROS_ERROR("[interaction_recognition] usage: interaction_recognition BAYESIAN_NET");
        exit(1);
    }

    InteractionRecognition foo;
    return foo.Main(argv[1]);

}

/**
 *  Dictionary to define commands because some annotations have typographic errors.
 *
 *  back    => 0
 *  follow  => 1
 *  forward => 2
 *  stop    => 3
 *  turn    => 4
 *  none    => 5
 *
 */

/**
 *  Dictionary to define gestures because some annotations have typographic errors.
 *
 *  fingertip_point => 0
 *  hand_point      => 1
 *  hold_item       => 2
 *  sweep_wave      => 3
 *  touch_full_hand => 4
 *  none            => 5
 *
 */

/**
 *  Dictionary to define the presentation category in object, region or workspace.
 *  Besides, unknown category is defined when it was not confirmed by the robot, or
 *  could cause ambiguity
 *
 *  object      => 0
 *  region      => 1
 *  workspace   => 2
 *  unknown     => 3
 *
 */
<commit_msg>[interaction_recognition] Little modifications in the output<commit_after>/*
 *      This node is used to perform inference in a Bayesian network.
 *      It's subscribed to interaction_monitor /BN_vars topic
 *      to obtain data and then performs inference with a BN using SMILE libraries
 *
 *      At the end it prints some statistics with the performance.
 *
 *      Lunds tekniska högskola | LTH 2015
 *      Felip Marti Carrillo
 *
 *      MIT License (MIT)
 *      Copyright (c) 2015 Felip Marti Carrillo
 */

#include "interaction_recognition_node.h"

InteractionRecognition::InteractionRecognition (void)
{

    // Init Subscriber
    this->sub = this->n.subscribe("BN_vars", 1,
                        &InteractionRecognition::perform_inference_callback, this);

    // Init Stats
    for (int i=0; i<9; i++) {
         Stats_Results[i]=0;
    }

    // Max difference to consider 2 objects different
    MAX_DIFF = 0.1;

    // Vector of strings with fails
    statsFails.resize(0);

}

InteractionRecognition::~InteractionRecognition (void)
{
}

void InteractionRecognition::perform_inference_callback
                        (const interaction_monitor::BayesianNetworkVariable& msg)
{

    if (msg.category==-1) {

        print_statistics();
        exit(0);

    }

    /// Performing Inference
    ROS_INFO("[Interaction_Recognition] Performing Inference");

    // Vars to get the handle of node
    int category = theNet.FindNode("category");
    int lastCommand = theNet.FindNode("lastCommand");
    int usrAnnounce = theNet.FindNode("usrAnnounce");
    int usrGesture = theNet.FindNode("usrGesture");
    int headingAdj = theNet.FindNode("headingAdj");
    int distanceAdj = theNet.FindNode("distanceAdj");

    // Vars to store data
    int lastCommandData=msg.last_cmd;
    int usrAnnounceData=msg.announce;
    int usrGestureData=msg.gesture;
    int headingAdjData=msg.head_adj;
    int distanceAdjData=msg.dist_adj;
    int categoryData=msg.category;

    // Setting evidence
    theNet.GetNode(lastCommand)->Value()->SetEvidence(lastCommandData);
    theNet.GetNode(usrAnnounce)->Value()->SetEvidence(usrAnnounceData);
    theNet.GetNode(usrGesture)->Value()->SetEvidence(usrGestureData);
    theNet.GetNode(headingAdj)->Value()->SetEvidence(headingAdjData);
    theNet.GetNode(distanceAdj)->Value()->SetEvidence(distanceAdjData);

    // Update the network
    theNet.UpdateBeliefs();

    // Get the result values
    DSL_sysCoordinates theCoordinates(*theNet.GetNode(category)->Value());
    DSL_idArray *theNames;
    theNames = theNet.GetNode(category)->Definition()->GetOutcomesNames();

    int objectIndex = theNames->FindPosition("object");
    int regionIndex = theNames->FindPosition("region");
    int workspaceIndex = theNames->FindPosition("workspace");
    int unknownIndex = theNames->FindPosition("unknown");

    // Probability of category
    double P_CategoryIs[4];
    // 0 P_CategoryIsObject
    // 1 P_CategoryIsRegion
    // 2 P_CategoryIsWorkspace
    // 3 P_CategoryIsUnknown

    theCoordinates[0] = objectIndex;
    theCoordinates.GoToCurrentPosition();
    // get P("category" = object)
    P_CategoryIs[0] = theCoordinates.UncheckedValue();
    ROS_INFO("[Interaction_Recognition] P(\"category\" = object) = %f",P_CategoryIs[0]);

    theCoordinates[0] = regionIndex;
    theCoordinates.GoToCurrentPosition();
    // get P("category" = region)
    P_CategoryIs[1] = theCoordinates.UncheckedValue();
    ROS_INFO("[Interaction_Recognition] P(\"category\" = region) = %f",P_CategoryIs[1]);

    theCoordinates[0] = workspaceIndex;
    theCoordinates.GoToCurrentPosition();
    // get P("category" = workspace)
    P_CategoryIs[2] = theCoordinates.UncheckedValue();
    ROS_INFO("[Interaction_Recognition] P(\"category\" = workspace) = %f",P_CategoryIs[2]);

    theCoordinates[0] = unknownIndex;
    theCoordinates.GoToCurrentPosition();
    // get P("category" = unknown)
    P_CategoryIs[3] = theCoordinates.UncheckedValue();
    ROS_INFO("[Interaction_Recognition] P(\"category\" = unknown) = %f",P_CategoryIs[3]);

    ROS_INFO("[Interaction_Recognition] User was presenting category %d", categoryData);

    /// UPDATING STATISTICS
    int CategoryWin=-1;
    double P_CategoryWin=-1;
    for (int i=0; i<4; i++) {
        if (P_CategoryWin < P_CategoryIs[i]) {
            P_CategoryWin = P_CategoryIs[i];
            CategoryWin=i;
        }
    }

    // Difference
    P_CategoryIs[0] = P_CategoryWin-P_CategoryIs[0];
    P_CategoryIs[1] = P_CategoryWin-P_CategoryIs[1];
    P_CategoryIs[2] = P_CategoryWin-P_CategoryIs[2];
    P_CategoryIs[3] = P_CategoryWin-P_CategoryIs[3];

    // Checking results category
    int equalCategory=0;
    std::vector<int> equalCategories;
    for (int i=0; i<4; i++) {
        if (P_CategoryIs[i]<MAX_DIFF) {
            equalCategory++;
            equalCategories.push_back(i);
        }
    }

    // Updating stats
    if (equalCategory==1 and categoryData == CategoryWin) {
        Stats_Results[0]++;     // Good Job!
    }
    else if (equalCategory==1 and categoryData != CategoryWin) {
        if (categoryData == 3) {
            Stats_Results[5]++;     // Nice, Unknown category classified!
        }
        else {
            Stats_Results[1]++;     // Mismatch!!
            std::ostringstream stringStream;
            stringStream << categoryData <<"->"
                         << CategoryWin;
            statsFails.push_back(stringStream.str());
        }
    }
    else if (equalCategory==2) {    // 2 are similar
        if ( P_CategoryIs[categoryData] < MAX_DIFF or categoryData == 3) {
            Stats_Results[2]++;     // Category is among them, or is Unknown
            std::ostringstream stringStream;
            stringStream << categoryData <<" -> "<< equalCategories[0]<<" or "
                         << equalCategories[1];
            statsBetween2.push_back(stringStream.str());
        }
        else {
            Stats_Results[6]++;     // Category NOT among them, FAIL
        }
    }
    else if (equalCategory==3) {    // 3 are similar
        if ( P_CategoryIs[categoryData] < MAX_DIFF or categoryData == 3) {
            Stats_Results[3]++;     // Category is among them, or is Unknown
            std::ostringstream stringStream;
            stringStream << categoryData <<" -> "<< equalCategories[0]<<", "
                         << equalCategories[1]<<" or " << equalCategories[2];
            statsAmong3.push_back(stringStream.str());
        }
        else {
            Stats_Results[7]++;     // Category NOT among them, FAIL
        }
    }
    else if (equalCategory==4) {    // all are similar
        Stats_Results[4]++;
    }
    else {
        Stats_Results[8]++;         //WTF??
    }

    // Clear the evidence in nodes
    theNet.GetNode(lastCommand)->Value()->ClearEvidence();
    theNet.GetNode(usrAnnounce)->Value()->ClearEvidence();
    theNet.GetNode(usrGesture)->Value()->ClearEvidence();
    theNet.GetNode(headingAdj)->Value()->ClearEvidence();
    theNet.GetNode(distanceAdj)->Value()->ClearEvidence();

    ROS_INFO("[Interaction_Recognition] * * * * ") ;

}

int InteractionRecognition::Main (const char* path) {

    // Open BN GENIE file
    if (theNet.ReadFile(path) != 0) {
        ROS_ERROR("[interaction_recognition] Cannot open the Bayesian Network");
        ROS_ERROR("[interaction_recognition] BN=\"%s\"",path);
        return 1;
    }
    else {
        ROS_INFO("[interaction_recognition] Bayesian Network opened successfully");
        ROS_INFO("[interaction_recognition] BN=\"%s\"",path);
    }

    // Wait for callbacks
    ros::spin();

}

void InteractionRecognition::print_statistics()
{

    ROS_WARN("[Interaction_Learner] No more data!");
    ROS_WARN("[Interaction_Learner] So, the node will be stopped gently :)");
    ROS_INFO("[Interaction_Learner] ********** STATISTICS **********");
    ROS_INFO("[Interaction_Learner] %d are OK!!", Stats_Results[0]);
    ROS_INFO("[Interaction_Learner] %d Mismatches!!", Stats_Results[1]);
    for (int i=0; i<statsFails.size(); i++) {
        std::cout<<statsFails[i]<<"; ";
    }
    std::cout<<std::endl;
    ROS_INFO("[Interaction_Learner] %d are Similar between 2", Stats_Results[2]);
    for (int i=0; i<statsBetween2.size(); i++) {
        std::cout<<statsBetween2[i]<<"; ";
    }
    std::cout<<std::endl;
    ROS_INFO("[Interaction_Learner] %d are Similar among 3", Stats_Results[3]);
    for (int i=0; i<statsAmong3.size(); i++) {
        std::cout<<statsAmong3[i]<<"; ";
    }
    std::cout<<std::endl;
    ROS_INFO("[Interaction_Learner] %d are Similar among 4", Stats_Results[4]);
    ROS_INFO("[Interaction_Learner] %d are Unknown classified", Stats_Results[5]);
    ROS_INFO("[Interaction_Learner] %d are Similar between 2, but FAIL", Stats_Results[6]);
    ROS_INFO("[Interaction_Learner] %d are Similar among 3, but FAIL", Stats_Results[7]);
    ROS_INFO("[Interaction_Learner] %d, if > 0, something weird is going on, CHECK CODE!!!",
                Stats_Results[8]);
    ROS_INFO("[Interaction_Learner] ********** ********** **********");

}

int main(int argc, char **argv)
{

    ros::init(argc, argv, "interaction_recognition");
    if (argc != 2) {
        ROS_ERROR("[interaction_recognition] usage: interaction_recognition BAYESIAN_NET");
        exit(1);
    }

    InteractionRecognition foo;
    return foo.Main(argv[1]);

}

/**
 *  Dictionary to define commands because some annotations have typographic errors.
 *
 *  back    => 0
 *  follow  => 1
 *  forward => 2
 *  stop    => 3
 *  turn    => 4
 *  none    => 5
 *
 */

/**
 *  Dictionary to define gestures because some annotations have typographic errors.
 *
 *  fingertip_point => 0
 *  hand_point      => 1
 *  hold_item       => 2
 *  sweep_wave      => 3
 *  touch_full_hand => 4
 *  none            => 5
 *
 */

/**
 *  Dictionary to define the presentation category in object, region or workspace.
 *  Besides, unknown category is defined when it was not confirmed by the robot, or
 *  could cause ambiguity
 *
 *  object      => 0
 *  region      => 1
 *  workspace   => 2
 *  unknown     => 3
 *
 */
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2019 Asylo authors
 *
 * 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 "asylo/platform/primitives/sgx/trusted_sgx.h"

#include <errno.h>

#include <vector>

#include "absl/strings/str_cat.h"
#include "asylo/util/logging.h"
#include "asylo/platform/arch/sgx/trusted/generated_bridge_t.h"
#include "asylo/platform/posix/threading/thread_manager.h"
#include "asylo/platform/primitives/extent.h"
#include "asylo/platform/primitives/primitive_status.h"
#include "asylo/platform/primitives/primitives.h"
#include "asylo/platform/primitives/sgx/sgx_error_space.h"
#include "asylo/platform/primitives/sgx/sgx_params.h"
#include "asylo/platform/primitives/trusted_primitives.h"
#include "asylo/platform/primitives/trusted_runtime.h"
#include "asylo/platform/primitives/util/message.h"
#include "asylo/platform/primitives/util/primitive_locks.h"
#include "asylo/platform/primitives/util/trusted_runtime_helper.h"
#include "asylo/platform/primitives/x86/spin_lock.h"
#include "asylo/util/cleanup.h"
#include "asylo/util/status.h"
#include "asylo/util/status_macros.h"
#include "include/sgx_trts.h"

extern "C" int enc_untrusted_puts(const char *message);

namespace asylo {
namespace primitives {

namespace {

#define CHECK_OCALL(status_)                                                 \
  do {                                                                       \
    sgx_status_t status##__COUNTER__ = status_;                              \
    if (status##__COUNTER__ != SGX_SUCCESS) {                                \
      TrustedPrimitives::BestEffortAbort(                                    \
          absl::StrCat(                                                      \
              __FILE__, ":", __LINE__, ": ",                                 \
              asylo::Status(status##__COUNTER__, "ocall failed").ToString()) \
              .c_str());                                                     \
    }                                                                        \
  } while (0)

}  // namespace

// Entry handler installed by the runtime to finalize the enclave at the time it
// is destroyed.
PrimitiveStatus FinalizeEnclave(void *context, MessageReader *in,
                                MessageWriter *out) {
  if (in) {
    ASYLO_RETURN_IF_READER_NOT_EMPTY(*in);
  }
  return asylo_enclave_fini();
}

// Entry handler installed by the runtime to start the created thread.
PrimitiveStatus DonateThread(void *context, MessageReader *in,
                             MessageWriter *out) {
  if (in) {
    ASYLO_RETURN_IF_READER_NOT_EMPTY(*in);
  }
  int result = 0;
  try {
    ThreadManager *thread_manager = ThreadManager::GetInstance();
    result = thread_manager->StartThread();
  } catch (...) {
    TrustedPrimitives::BestEffortAbort(
        "Uncaught exception in enclave entry handler: DonateThread. Failed to "
        "get ThreadManager instance or start the thread.");
  }
  return PrimitiveStatus(result);
}

// Registers internal handlers, including entry handlers.
void RegisterInternalHandlers() {
  // Register the enclave donate thread entry handler.
  if (!TrustedPrimitives::RegisterEntryHandler(kSelectorAsyloDonateThread,
                                               EntryHandler{DonateThread})
           .ok()) {
    TrustedPrimitives::BestEffortAbort(
        "Could not register entry handler: DonateThread.");
  }

  // Register the enclave finalization entry handler.
  if (!TrustedPrimitives::RegisterEntryHandler(kSelectorAsyloFini,
                                               EntryHandler{FinalizeEnclave})
           .ok()) {
    TrustedPrimitives::BestEffortAbort(
        "Could not register entry handler: FinalizeEnclave");
  }
}

void TrustedPrimitives::BestEffortAbort(const char *message) {
  DebugPuts(message);
  enc_block_ecalls();
  MarkEnclaveAborted();
  abort();
}

PrimitiveStatus TrustedPrimitives::RegisterEntryHandler(
    uint64_t selector, const EntryHandler &handler) {
  return asylo::primitives::RegisterEntryHandler(selector, handler);
}

int asylo_enclave_call(uint64_t selector, void *buffer) {
  SgxParams *const sgx_params = reinterpret_cast<SgxParams *>(buffer);

  const void *input = sgx_params->input;
  size_t input_size = sgx_params->input_size;
  sgx_params->input = nullptr;
  sgx_params->input_size = 0;
  void *output = nullptr;
  size_t output_size = 0;

  if (input) {
    if (TrustedPrimitives::IsTrustedExtent(input, input_size)) {
      PrimitiveStatus status{error::GoogleError::INVALID_ARGUMENT,
                             "input should lie within untrusted memory."};
      return status.error_code();
    }
    if (input_size > 0) {
      // Copy untrusted |input| to trusted memory and pass that as input.
      void *trusted_input = malloc(input_size);
      memcpy(trusted_input, input, input_size);
      TrustedPrimitives::UntrustedLocalFree(const_cast<void *>(input));
      input = trusted_input;
    } else {
      TrustedPrimitives::UntrustedLocalFree(const_cast<void *>(input));
      input = nullptr;
    }
  }

  PrimitiveStatus status =
      InvokeEntryHandler(selector, input, input_size, &output, &output_size);

  if (output) {
    // Copy trusted |*output| to untrusted memory and pass that as
    // output. We also free trusted |*output| after it is copied to untrusted
    // side. The untrusted caller is still responsible for freeing |*output|,
    // which now points to untrusted memory.
    if (!TrustedPrimitives::IsTrustedExtent(output, output_size)) {
      PrimitiveStatus{error::GoogleError::INVALID_ARGUMENT,
                      "output should lie in trusted memory"};
      return status.error_code();
    }

    void *untrusted_output =
        TrustedPrimitives::UntrustedLocalAlloc(output_size);
    memcpy(untrusted_output, output, output_size);
    free(output);
    output = untrusted_output;
  }

  sgx_params->output = output;
  sgx_params->output_size = static_cast<uint64_t>(output_size);
  return status.error_code();
}

// For SGX, UntrustedLocalAlloc uses malloc() on the untrusted host to
// allocate memory.
void *TrustedPrimitives::UntrustedLocalAlloc(size_t size) noexcept {
  void *result;
  CHECK_OCALL(
      ocall_untrusted_local_alloc(&result, static_cast<uint64_t>(size)));
  if (result && !sgx_is_outside_enclave(result, static_cast<uint64_t>(size))) {
    abort();
  }

  // On error, malloc returns nullptr and sets errno to ENOMEM.
  if (!result) {
    errno = ENOMEM;
    TrustedPrimitives::DebugPuts("UntrustedLocalAlloc on SGX failed.");
  }
  return result;
}

// For SGX, UntrustedLocalFree uses free() on the untrusted host to free the
// memory allocated by UntrustedLocalAlloc.
void TrustedPrimitives::UntrustedLocalFree(void *ptr) noexcept {
  CHECK_OCALL(ocall_untrusted_local_free(ptr));
}

bool TrustedPrimitives::IsTrustedExtent(const void *addr, size_t size) {
  return enc_is_within_enclave(addr, size);
}

void TrustedPrimitives::DebugPuts(const char *message) {
  enc_untrusted_puts(message);
}

PrimitiveStatus TrustedPrimitives::UntrustedCall(uint64_t untrusted_selector,
                                                 MessageWriter *input,
                                                 MessageReader *output) {
  int ret;

  SgxParams *const sgx_params = reinterpret_cast<SgxParams *>(
      TrustedPrimitives::UntrustedLocalAlloc(sizeof(SgxParams)));
  Cleanup clean_up(
      [sgx_params] { TrustedPrimitives::UntrustedLocalFree(sgx_params); });
  sgx_params->input_size = 0;
  sgx_params->input = nullptr;
  if (input) {
    sgx_params->input_size = input->MessageSize();
    if (sgx_params->input_size > 0) {
      sgx_params->input =
          TrustedPrimitives::UntrustedLocalAlloc(sgx_params->input_size);
      // Copy data to |input_buffer|.
      input->Serialize(const_cast<void *>(sgx_params->input));
    }
  }
  sgx_params->output_size = 0;
  sgx_params->output = nullptr;
  CHECK_OCALL(
      ocall_dispatch_untrusted_call(&ret, untrusted_selector, sgx_params));
  if (sgx_params->output) {
    // For the results obtained in |output_buffer|, copy them to |output|
    // before freeing the buffer.
    output->Deserialize(sgx_params->output, sgx_params->output_size);
    TrustedPrimitives::UntrustedLocalFree(sgx_params->output);
  }
  return PrimitiveStatus::OkStatus();
}

// For SGX, CreateThread() needs to exit the enclave by making an UntrustedCall
// to CreateThreadHandler, which makes an EnclaveCall to enter the enclave with
// the new thread and register it with the thread manager and execute the
// intended callback.
int TrustedPrimitives::CreateThread() {
  MessageWriter input;
  MessageReader output;
  PrimitiveStatus status =
      UntrustedCall(kSelectorCreateThread, &input, &output);
  if (!status.ok()) {
    DebugPuts("CreateThread failed.");
    return -1;
  }
  if (output.size() != 1) {
    DebugPuts("CreateThread error: unexpected output size received.");
    abort();
  }
  return output.next<int>();
}

}  // namespace primitives
}  // namespace asylo
<commit_msg>Use trusted runtime for memory bounds checking<commit_after>/*
 *
 * Copyright 2019 Asylo authors
 *
 * 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 "asylo/platform/primitives/sgx/trusted_sgx.h"

#include <errno.h>

#include <vector>

#include "absl/strings/str_cat.h"
#include "asylo/util/logging.h"
#include "asylo/platform/arch/sgx/trusted/generated_bridge_t.h"
#include "asylo/platform/posix/threading/thread_manager.h"
#include "asylo/platform/primitives/extent.h"
#include "asylo/platform/primitives/primitive_status.h"
#include "asylo/platform/primitives/primitives.h"
#include "asylo/platform/primitives/sgx/sgx_error_space.h"
#include "asylo/platform/primitives/sgx/sgx_params.h"
#include "asylo/platform/primitives/trusted_primitives.h"
#include "asylo/platform/primitives/trusted_runtime.h"
#include "asylo/platform/primitives/util/message.h"
#include "asylo/platform/primitives/util/primitive_locks.h"
#include "asylo/platform/primitives/util/trusted_runtime_helper.h"
#include "asylo/platform/primitives/x86/spin_lock.h"
#include "asylo/util/cleanup.h"
#include "asylo/util/status.h"
#include "asylo/util/status_macros.h"
#include "include/sgx_trts.h"

extern "C" int enc_untrusted_puts(const char *message);

namespace asylo {
namespace primitives {

namespace {

#define CHECK_OCALL(status_)                                                 \
  do {                                                                       \
    sgx_status_t status##__COUNTER__ = status_;                              \
    if (status##__COUNTER__ != SGX_SUCCESS) {                                \
      TrustedPrimitives::BestEffortAbort(                                    \
          absl::StrCat(                                                      \
              __FILE__, ":", __LINE__, ": ",                                 \
              asylo::Status(status##__COUNTER__, "ocall failed").ToString()) \
              .c_str());                                                     \
    }                                                                        \
  } while (0)

}  // namespace

// Entry handler installed by the runtime to finalize the enclave at the time it
// is destroyed.
PrimitiveStatus FinalizeEnclave(void *context, MessageReader *in,
                                MessageWriter *out) {
  if (in) {
    ASYLO_RETURN_IF_READER_NOT_EMPTY(*in);
  }
  return asylo_enclave_fini();
}

// Entry handler installed by the runtime to start the created thread.
PrimitiveStatus DonateThread(void *context, MessageReader *in,
                             MessageWriter *out) {
  if (in) {
    ASYLO_RETURN_IF_READER_NOT_EMPTY(*in);
  }
  int result = 0;
  try {
    ThreadManager *thread_manager = ThreadManager::GetInstance();
    result = thread_manager->StartThread();
  } catch (...) {
    TrustedPrimitives::BestEffortAbort(
        "Uncaught exception in enclave entry handler: DonateThread. Failed to "
        "get ThreadManager instance or start the thread.");
  }
  return PrimitiveStatus(result);
}

// Registers internal handlers, including entry handlers.
void RegisterInternalHandlers() {
  // Register the enclave donate thread entry handler.
  if (!TrustedPrimitives::RegisterEntryHandler(kSelectorAsyloDonateThread,
                                               EntryHandler{DonateThread})
           .ok()) {
    TrustedPrimitives::BestEffortAbort(
        "Could not register entry handler: DonateThread.");
  }

  // Register the enclave finalization entry handler.
  if (!TrustedPrimitives::RegisterEntryHandler(kSelectorAsyloFini,
                                               EntryHandler{FinalizeEnclave})
           .ok()) {
    TrustedPrimitives::BestEffortAbort(
        "Could not register entry handler: FinalizeEnclave");
  }
}

void TrustedPrimitives::BestEffortAbort(const char *message) {
  DebugPuts(message);
  enc_block_ecalls();
  MarkEnclaveAborted();
  abort();
}

PrimitiveStatus TrustedPrimitives::RegisterEntryHandler(
    uint64_t selector, const EntryHandler &handler) {
  return asylo::primitives::RegisterEntryHandler(selector, handler);
}

int asylo_enclave_call(uint64_t selector, void *buffer) {
  SgxParams *const sgx_params = reinterpret_cast<SgxParams *>(buffer);

  const void *input = sgx_params->input;
  size_t input_size = sgx_params->input_size;
  sgx_params->input = nullptr;
  sgx_params->input_size = 0;
  void *output = nullptr;
  size_t output_size = 0;

  if (input) {
    if (TrustedPrimitives::IsTrustedExtent(input, input_size)) {
      PrimitiveStatus status{error::GoogleError::INVALID_ARGUMENT,
                             "input should lie within untrusted memory."};
      return status.error_code();
    }
    if (input_size > 0) {
      // Copy untrusted |input| to trusted memory and pass that as input.
      void *trusted_input = malloc(input_size);
      memcpy(trusted_input, input, input_size);
      TrustedPrimitives::UntrustedLocalFree(const_cast<void *>(input));
      input = trusted_input;
    } else {
      TrustedPrimitives::UntrustedLocalFree(const_cast<void *>(input));
      input = nullptr;
    }
  }

  PrimitiveStatus status =
      InvokeEntryHandler(selector, input, input_size, &output, &output_size);

  if (output) {
    // Copy trusted |*output| to untrusted memory and pass that as
    // output. We also free trusted |*output| after it is copied to untrusted
    // side. The untrusted caller is still responsible for freeing |*output|,
    // which now points to untrusted memory.
    if (!TrustedPrimitives::IsTrustedExtent(output, output_size)) {
      PrimitiveStatus{error::GoogleError::INVALID_ARGUMENT,
                      "output should lie in trusted memory"};
      return status.error_code();
    }

    void *untrusted_output =
        TrustedPrimitives::UntrustedLocalAlloc(output_size);
    memcpy(untrusted_output, output, output_size);
    free(output);
    output = untrusted_output;
  }

  sgx_params->output = output;
  sgx_params->output_size = static_cast<uint64_t>(output_size);
  return status.error_code();
}

// For SGX, UntrustedLocalAlloc uses malloc() on the untrusted host to
// allocate memory.
void *TrustedPrimitives::UntrustedLocalAlloc(size_t size) noexcept {
  void *result;
  CHECK_OCALL(
      ocall_untrusted_local_alloc(&result, static_cast<uint64_t>(size)));
  if (result && !enc_is_outside_enclave(result, static_cast<uint64_t>(size))) {
    abort();
  }

  // On error, malloc returns nullptr and sets errno to ENOMEM.
  if (!result) {
    errno = ENOMEM;
    TrustedPrimitives::DebugPuts("UntrustedLocalAlloc on SGX failed.");
  }
  return result;
}

// For SGX, UntrustedLocalFree uses free() on the untrusted host to free the
// memory allocated by UntrustedLocalAlloc.
void TrustedPrimitives::UntrustedLocalFree(void *ptr) noexcept {
  CHECK_OCALL(ocall_untrusted_local_free(ptr));
}

bool TrustedPrimitives::IsTrustedExtent(const void *addr, size_t size) {
  return enc_is_within_enclave(addr, size);
}

void TrustedPrimitives::DebugPuts(const char *message) {
  enc_untrusted_puts(message);
}

PrimitiveStatus TrustedPrimitives::UntrustedCall(uint64_t untrusted_selector,
                                                 MessageWriter *input,
                                                 MessageReader *output) {
  int ret;

  SgxParams *const sgx_params = reinterpret_cast<SgxParams *>(
      TrustedPrimitives::UntrustedLocalAlloc(sizeof(SgxParams)));
  Cleanup clean_up(
      [sgx_params] { TrustedPrimitives::UntrustedLocalFree(sgx_params); });
  sgx_params->input_size = 0;
  sgx_params->input = nullptr;
  if (input) {
    sgx_params->input_size = input->MessageSize();
    if (sgx_params->input_size > 0) {
      sgx_params->input =
          TrustedPrimitives::UntrustedLocalAlloc(sgx_params->input_size);
      // Copy data to |input_buffer|.
      input->Serialize(const_cast<void *>(sgx_params->input));
    }
  }
  sgx_params->output_size = 0;
  sgx_params->output = nullptr;
  CHECK_OCALL(
      ocall_dispatch_untrusted_call(&ret, untrusted_selector, sgx_params));
  if (sgx_params->output) {
    // For the results obtained in |output_buffer|, copy them to |output|
    // before freeing the buffer.
    output->Deserialize(sgx_params->output, sgx_params->output_size);
    TrustedPrimitives::UntrustedLocalFree(sgx_params->output);
  }
  return PrimitiveStatus::OkStatus();
}

// For SGX, CreateThread() needs to exit the enclave by making an UntrustedCall
// to CreateThreadHandler, which makes an EnclaveCall to enter the enclave with
// the new thread and register it with the thread manager and execute the
// intended callback.
int TrustedPrimitives::CreateThread() {
  MessageWriter input;
  MessageReader output;
  PrimitiveStatus status =
      UntrustedCall(kSelectorCreateThread, &input, &output);
  if (!status.ok()) {
    DebugPuts("CreateThread failed.");
    return -1;
  }
  if (output.size() != 1) {
    DebugPuts("CreateThread error: unexpected output size received.");
    abort();
  }
  return output.next<int>();
}

}  // namespace primitives
}  // namespace asylo
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#include "gstframegrabber.hxx"
#include "gstplayer.hxx"

#include <gst/gstbuffer.h>
#include <gst/video/video.h>
#include <gst/video/gstvideosink.h>

#include <vcl/graph.hxx>
#include <vcl/bmpacc.hxx>

#include <string>

#ifdef AVMEDIA_GST_0_10
#  define AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.FrameGrabber_GStreamer_0_10"
#  define AVMEDIA_GST_FRAMEGRABBER_SERVICENAME "com.sun.star.media.FrameGrabber_GStreamer_0_10"
#else
#  define AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.FrameGrabber_GStreamer"
#  define AVMEDIA_GST_FRAMEGRABBER_SERVICENAME "com.sun.star.media.FrameGrabber_GStreamer"
#endif

using namespace ::com::sun::star;

namespace avmedia { namespace gstreamer {

void FrameGrabber::disposePipeline()
{
    if( mpPipeline != NULL )
    {
        gst_element_set_state( mpPipeline, GST_STATE_NULL );
        g_object_unref( G_OBJECT( mpPipeline ) );
        mpPipeline = NULL;
    }
}

FrameGrabber::FrameGrabber( const OUString &rURL ) :
    FrameGrabber_BASE()
{
    gchar *pPipelineStr;
    pPipelineStr = g_strdup_printf(
#ifdef AVMEDIA_GST_0_10
        "uridecodebin uri=%s ! ffmpegcolorspace ! videoscale ! appsink "
        "name=sink caps=\"video/x-raw-rgb,format=RGB,pixel-aspect-ratio=1/1,"
        "bpp=(int)24,depth=(int)24,endianness=(int)4321,"
        "red_mask=(int)0xff0000, green_mask=(int)0x00ff00, blue_mask=(int)0x0000ff\"",
#else
        "uridecodebin uri=%s ! videoconvert ! videoscale ! appsink "
        "name=sink caps=\"video/x-raw,format=RGB,pixel-aspect-ratio=1/1\"",
#endif
        rtl::OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr() );

    GError *pError = NULL;
    mpPipeline = gst_parse_launch( pPipelineStr, &pError );
    if( pError != NULL) {
        g_warning( "Failed to construct frame-grabber pipeline '%s'\n", pError->message );
        g_error_free( pError );
        disposePipeline();
    }

    if( mpPipeline ) {
        // pre-roll
        switch( gst_element_set_state( mpPipeline, GST_STATE_PAUSED ) ) {
        case GST_STATE_CHANGE_FAILURE:
        case GST_STATE_CHANGE_NO_PREROLL:
            g_warning( "failure pre-rolling media" );
            disposePipeline();
            break;
        default:
            break;
        }
    }
    if( mpPipeline &&
        gst_element_get_state( mpPipeline, NULL, NULL, 5 * GST_SECOND ) == GST_STATE_CHANGE_FAILURE )
        disposePipeline();
}

FrameGrabber::~FrameGrabber()
{
    disposePipeline();
}

FrameGrabber* FrameGrabber::create( const OUString &rURL )
{
    return new FrameGrabber( rURL );
}

uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime )
    throw (uno::RuntimeException)
{
    uno::Reference< graphic::XGraphic > xRet;

    if( !mpPipeline )
        return xRet;

    gint64 gst_position = llround( fMediaTime * 1E9 );
    gst_element_seek_simple(
        mpPipeline, GST_FORMAT_TIME,
        (GstSeekFlags)(GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH),
        gst_position );

    GstElement *pSink = gst_bin_get_by_name( GST_BIN( mpPipeline ), "sink" );
    if( !pSink )
        return xRet;

    GstBuffer *pBuf = NULL;
    GstCaps *pCaps = NULL;

    // synchronously fetch the frame
#ifdef AVMEDIA_GST_0_10
    g_signal_emit_by_name( pSink, "pull-preroll", &pBuf, NULL );
    if( pBuf )
        pCaps = GST_BUFFER_CAPS( pBuf );
#else
    GstSample *pSample = NULL;
    g_signal_emit_by_name( pSink, "pull-preroll", &pSample, NULL );

    if( pSample )
    {
        pBuf = gst_sample_get_buffer( pSample );
        pCaps = gst_sample_get_caps( pSample );
    }
#endif

    // get geometry
    int nWidth = 0, nHeight = 0;
    if( !pCaps )
        g_warning( "could not get snapshot format\n" );
    else
    {
        GstStructure *pStruct = gst_caps_get_structure( pCaps, 0 );

        /* we need to get the final caps on the buffer to get the size */
        if( !gst_structure_get_int( pStruct, "width", &nWidth ) ||
            !gst_structure_get_int( pStruct, "height", &nHeight ) )
            nWidth = nHeight = 0;
    }

    if( pBuf && nWidth > 0 && nHeight > 0 &&
        // sanity check the size
#ifdef AVMEDIA_GST_0_10
        GST_BUFFER_SIZE( pBuf ) >= static_cast<unsigned>( nWidth * nHeight * 3 )
#else
        gst_buffer_get_size( pBuf ) >= ( nWidth * nHeight * 3 )
#endif
        )
    {
        sal_uInt8 *pData = NULL;
#ifdef AVMEDIA_GST_0_10
        pData = GST_BUFFER_DATA( pBuf );
#else
        GstMapInfo aMapInfo;
        gst_buffer_map( pBuf, &aMapInfo, GST_MAP_READ );
        pData = aMapInfo.data;
#endif

        int nStride = GST_ROUND_UP_4( nWidth * 3 );
        Bitmap aBmp( Size( nWidth, nHeight ), 24 );

        BitmapWriteAccess *pWrite = aBmp.AcquireWriteAccess();
        if( pWrite )
        {
            // yet another cheesy pixel copying loop
            for( int y = 0; y < nHeight; ++y )
            {
                sal_uInt8 *p = pData + y * nStride;
                for( int x = 0; x < nWidth; ++x )
                {
                    BitmapColor col( p[0], p[1], p[2] );
                    pWrite->SetPixel( y, x, col );
                    p += 3;
                }
            }
        }
        aBmp.ReleaseAccess( pWrite );

#ifndef AVMEDIA_GST_0_10
        gst_buffer_unmap( pBuf, &aMapInfo );
#endif

        xRet = Graphic( aBmp ).GetXGraphic();
    }

    return xRet;
}

OUString SAL_CALL FrameGrabber::getImplementationName(  )
    throw (uno::RuntimeException)
{
    return OUString( AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME );
}

sal_Bool SAL_CALL FrameGrabber::supportsService( const OUString& ServiceName )
    throw (uno::RuntimeException)
{
    return ServiceName == AVMEDIA_GST_FRAMEGRABBER_SERVICENAME;
}

uno::Sequence< OUString > SAL_CALL FrameGrabber::getSupportedServiceNames()
    throw (uno::RuntimeException)
{
    uno::Sequence< OUString > aRet(1);
    aRet[0] = AVMEDIA_GST_FRAMEGRABBER_SERVICENAME;

    return aRet;
}

} // namespace gstreamer
} // namespace avmedia

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>-Werror,-Wsign-compare<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#include "gstframegrabber.hxx"
#include "gstplayer.hxx"

#include <gst/gstbuffer.h>
#include <gst/video/video.h>
#include <gst/video/gstvideosink.h>

#include <vcl/graph.hxx>
#include <vcl/bmpacc.hxx>

#include <string>

#ifdef AVMEDIA_GST_0_10
#  define AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.FrameGrabber_GStreamer_0_10"
#  define AVMEDIA_GST_FRAMEGRABBER_SERVICENAME "com.sun.star.media.FrameGrabber_GStreamer_0_10"
#else
#  define AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.FrameGrabber_GStreamer"
#  define AVMEDIA_GST_FRAMEGRABBER_SERVICENAME "com.sun.star.media.FrameGrabber_GStreamer"
#endif

using namespace ::com::sun::star;

namespace avmedia { namespace gstreamer {

void FrameGrabber::disposePipeline()
{
    if( mpPipeline != NULL )
    {
        gst_element_set_state( mpPipeline, GST_STATE_NULL );
        g_object_unref( G_OBJECT( mpPipeline ) );
        mpPipeline = NULL;
    }
}

FrameGrabber::FrameGrabber( const OUString &rURL ) :
    FrameGrabber_BASE()
{
    gchar *pPipelineStr;
    pPipelineStr = g_strdup_printf(
#ifdef AVMEDIA_GST_0_10
        "uridecodebin uri=%s ! ffmpegcolorspace ! videoscale ! appsink "
        "name=sink caps=\"video/x-raw-rgb,format=RGB,pixel-aspect-ratio=1/1,"
        "bpp=(int)24,depth=(int)24,endianness=(int)4321,"
        "red_mask=(int)0xff0000, green_mask=(int)0x00ff00, blue_mask=(int)0x0000ff\"",
#else
        "uridecodebin uri=%s ! videoconvert ! videoscale ! appsink "
        "name=sink caps=\"video/x-raw,format=RGB,pixel-aspect-ratio=1/1\"",
#endif
        rtl::OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr() );

    GError *pError = NULL;
    mpPipeline = gst_parse_launch( pPipelineStr, &pError );
    if( pError != NULL) {
        g_warning( "Failed to construct frame-grabber pipeline '%s'\n", pError->message );
        g_error_free( pError );
        disposePipeline();
    }

    if( mpPipeline ) {
        // pre-roll
        switch( gst_element_set_state( mpPipeline, GST_STATE_PAUSED ) ) {
        case GST_STATE_CHANGE_FAILURE:
        case GST_STATE_CHANGE_NO_PREROLL:
            g_warning( "failure pre-rolling media" );
            disposePipeline();
            break;
        default:
            break;
        }
    }
    if( mpPipeline &&
        gst_element_get_state( mpPipeline, NULL, NULL, 5 * GST_SECOND ) == GST_STATE_CHANGE_FAILURE )
        disposePipeline();
}

FrameGrabber::~FrameGrabber()
{
    disposePipeline();
}

FrameGrabber* FrameGrabber::create( const OUString &rURL )
{
    return new FrameGrabber( rURL );
}

uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime )
    throw (uno::RuntimeException)
{
    uno::Reference< graphic::XGraphic > xRet;

    if( !mpPipeline )
        return xRet;

    gint64 gst_position = llround( fMediaTime * 1E9 );
    gst_element_seek_simple(
        mpPipeline, GST_FORMAT_TIME,
        (GstSeekFlags)(GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH),
        gst_position );

    GstElement *pSink = gst_bin_get_by_name( GST_BIN( mpPipeline ), "sink" );
    if( !pSink )
        return xRet;

    GstBuffer *pBuf = NULL;
    GstCaps *pCaps = NULL;

    // synchronously fetch the frame
#ifdef AVMEDIA_GST_0_10
    g_signal_emit_by_name( pSink, "pull-preroll", &pBuf, NULL );
    if( pBuf )
        pCaps = GST_BUFFER_CAPS( pBuf );
#else
    GstSample *pSample = NULL;
    g_signal_emit_by_name( pSink, "pull-preroll", &pSample, NULL );

    if( pSample )
    {
        pBuf = gst_sample_get_buffer( pSample );
        pCaps = gst_sample_get_caps( pSample );
    }
#endif

    // get geometry
    int nWidth = 0, nHeight = 0;
    if( !pCaps )
        g_warning( "could not get snapshot format\n" );
    else
    {
        GstStructure *pStruct = gst_caps_get_structure( pCaps, 0 );

        /* we need to get the final caps on the buffer to get the size */
        if( !gst_structure_get_int( pStruct, "width", &nWidth ) ||
            !gst_structure_get_int( pStruct, "height", &nHeight ) )
            nWidth = nHeight = 0;
    }

    if( pBuf && nWidth > 0 && nHeight > 0 &&
        // sanity check the size
#ifdef AVMEDIA_GST_0_10
        GST_BUFFER_SIZE( pBuf ) >= static_cast<unsigned>( nWidth * nHeight * 3 )
#else
        gst_buffer_get_size( pBuf ) >= static_cast<unsigned>( nWidth * nHeight * 3 )
#endif
        )
    {
        sal_uInt8 *pData = NULL;
#ifdef AVMEDIA_GST_0_10
        pData = GST_BUFFER_DATA( pBuf );
#else
        GstMapInfo aMapInfo;
        gst_buffer_map( pBuf, &aMapInfo, GST_MAP_READ );
        pData = aMapInfo.data;
#endif

        int nStride = GST_ROUND_UP_4( nWidth * 3 );
        Bitmap aBmp( Size( nWidth, nHeight ), 24 );

        BitmapWriteAccess *pWrite = aBmp.AcquireWriteAccess();
        if( pWrite )
        {
            // yet another cheesy pixel copying loop
            for( int y = 0; y < nHeight; ++y )
            {
                sal_uInt8 *p = pData + y * nStride;
                for( int x = 0; x < nWidth; ++x )
                {
                    BitmapColor col( p[0], p[1], p[2] );
                    pWrite->SetPixel( y, x, col );
                    p += 3;
                }
            }
        }
        aBmp.ReleaseAccess( pWrite );

#ifndef AVMEDIA_GST_0_10
        gst_buffer_unmap( pBuf, &aMapInfo );
#endif

        xRet = Graphic( aBmp ).GetXGraphic();
    }

    return xRet;
}

OUString SAL_CALL FrameGrabber::getImplementationName(  )
    throw (uno::RuntimeException)
{
    return OUString( AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME );
}

sal_Bool SAL_CALL FrameGrabber::supportsService( const OUString& ServiceName )
    throw (uno::RuntimeException)
{
    return ServiceName == AVMEDIA_GST_FRAMEGRABBER_SERVICENAME;
}

uno::Sequence< OUString > SAL_CALL FrameGrabber::getSupportedServiceNames()
    throw (uno::RuntimeException)
{
    uno::Sequence< OUString > aRet(1);
    aRet[0] = AVMEDIA_GST_FRAMEGRABBER_SERVICENAME;

    return aRet;
}

} // namespace gstreamer
} // namespace avmedia

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#include "Network.hpp"

#include <chrono>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <memory>

#include <enet/enet.h>

#include <meiose/msgpack.hpp>
#include <meiose/variant.hpp>

#include "../crypto/Random.hpp"
#include "../util/Log.hpp"
#include "msgtypes/ConnectionParam.hpp"

#include <iomanip>

struct membuf : std::streambuf {
  membuf(char* begin, char* end) {
    setg(begin, begin, end);
  }
  membuf(void* begin, void* end) :
    membuf(reinterpret_cast<char*>(begin), reinterpret_cast<char*>(end)) {}
  membuf(const void* begin, const void* end) :
    membuf(const_cast<void*>(begin), const_cast<void*>(end)) {}
};
class omsgbuf : public std::streambuf {
protected:
  diggler::net::OutMessage &omsg;
public:
  omsgbuf(diggler::net::OutMessage &o) : omsg(o) {}
protected:
  std::streamsize xsputn(const char_type* s, std::streamsize n) override {
    omsg.writeData(s, n);
    return n;
  }
  int_type overflow(int_type ch) override {
    omsg.writeI8(ch);
    return 1;
  }
};

namespace diggler {
namespace net {

using Util::Log;
using namespace Util::Logging::LogLevels;

static const char *TAG = "Network";

static bool InitDone = false;

bool Init() {
  if (InitDone)
    return true;
  InitDone = true;
  return enet_initialize() == 0;
}

void DeInit() {
  enet_deinitialize();
}

std::string GetNetworkLibVersion() {
  ENetVersion ver = enet_linked_version();
  std::ostringstream sstm;
  sstm << "ENet linked " << ENET_VERSION_MAJOR << '.' << ENET_VERSION_MINOR
    << '.' << ENET_VERSION_PATCH << ", using " << ENET_VERSION_GET_MAJOR(ver)
    << '.' << ENET_VERSION_GET_MINOR(ver) << '.' << ENET_VERSION_GET_PATCH(ver);
  return sstm.str();
}

static enet_uint32 TferToFlags(Tfer mode) {
  switch (mode) {
  case Tfer::Rel:
    return ENET_PACKET_FLAG_RELIABLE;
  case Tfer::Unseq:
    return ENET_PACKET_FLAG_UNSEQUENCED;
  case Tfer::Unrel:
    break;
  }
  return 0;
}

Message::Message(MessageType t, uint8 s) :
  MemoryStream(nullptr, 0),
  m_type(t),
  m_subtype(s) {
}

InMessage::InMessage() :
  Message(MessageType::Null, 0),
  m_chan(Channels::Base) {
}

InMessage::~InMessage() {
  free();
}

void InMessage::setType(MessageType type) {
  free();
  m_type = type;
  m_subtype = m_length = m_cursor = 0;
  m_data = nullptr;
}

void InMessage::fromData(const void *data, SizeT len, Channels chan) {
  if (len < HeaderSize) {
    throw std::invalid_argument("Message length is smaller than message header");
  }
  const uint8 *const bytes = static_cast<const uint8*>(data);
  free();
  m_chan = chan;
  m_cursor = 0;
  m_length = len - HeaderSize;
  m_type = static_cast<MessageType>(bytes[0]);
  m_subtype = bytes[1];
  // m_data/bytes is guaranteed never to be written to, so we can const_cast it
  m_data = const_cast<uint8*>(bytes) + HeaderSize;
}

void InMessage::free() {
  if (m_data != nullptr) {
    delete[] (m_data - HeaderSize);
  }
  m_type = MessageType::Null;
  m_subtype = m_length = m_cursor = 0;
  m_data = nullptr;
}

void InMessage::readMsgpack(meiose::variant &var) {
  uint32 len = readU32();
  if (len > remaining()) {
    throw std::runtime_error("Not enough bytes available for reported msgpack length");
  }
  const void *start = getCursorPtr(len);
  membuf sbuf(start, getCursorPtr());
  std::istream in(&sbuf);
  meiose::msgpack::read(in, var);
}

Channels InMessage::getChannel() const {
  return m_chan;
}

OutMessage::OutMessage(MessageType t, uint8 subtype) :
  Message(t, subtype),
  m_actualData(nullptr) {
}

OutMessage::~OutMessage() {
  std::free(m_actualData);
  m_data = nullptr;
}

const static int OutMessage_AllocStep = 1024;
void OutMessage::fit(SizeT len) {
  if (len <= m_allocated)
    return;
  SizeT targetSize = ((len + OutMessage_AllocStep - 1) /
    OutMessage_AllocStep)*OutMessage_AllocStep; // Round up
  using DataT = decltype(m_actualData);
  DataT newActualData = static_cast<DataT>(
    std::realloc(m_actualData, HeaderSize + targetSize));
  if (newActualData == nullptr)
    throw std::bad_alloc();
  m_actualData = newActualData;
  m_data = newActualData + HeaderSize;
  m_allocated = targetSize;
}

void OutMessage::writeMsgpack(const meiose::variant &var) {
  PosT pos = tell();
  writeU32(0);
  omsgbuf sbuf(*this);
  std::ostream out(&sbuf);
  meiose::msgpack::write(out, var);
  PosT posWritten = tell();
  seek(pos);
  writeU32(static_cast<uint32>(posWritten - (pos + sizeof(uint32))));
  seek(posWritten);
}

glm::vec3 InMessage::readVec3() {
  float x, y, z;
  x = readFloat();
  y = readFloat();
  z = readFloat();
  return glm::vec3(x, y, z);
}

glm::ivec3 InMessage::readIVec3() {
  int32 x, y, z;
  x = readI32();
  y = readI32();
  z = readI32();
  return glm::ivec3(x, y, z);
}

Peer::Peer(Host &host, void *peer) :
  host(host),
  peer(peer) {
  reinterpret_cast<ENetPeer*>(this->peer)->data = this;
  Crypto::Random::randomData(connectionPk);
  Crypto::DiffieHellman::scalarmultBase(connectionSk, connectionPk);
}

bool Peer::operator==(const Peer &other) const {
  return peer == other.peer;
}

bool Peer::operator!=(const Peer &other) const {
  return !(*this == other);
}

void Peer::disconnect(uint32 data) {
  ENetPeer *const peer = reinterpret_cast<ENetPeer*>(this->peer);
  enet_peer_disconnect(peer, data);
}

std::string Peer::peerHost() {
  const ENetPeer *const peer = reinterpret_cast<const ENetPeer*>(this->peer);
  std::ostringstream oss;
  char *chars = new char[512];
  enet_address_get_host_ip(&peer->host->address, chars, 512);
  oss << chars;
  delete[] chars;
  oss << ':' << peer->host->address.port;
  return oss.str();
}

std::string Peer::peerIP() {
  const ENetPeer *const peer = reinterpret_cast<const ENetPeer*>(this->peer);
  char *chars = new char[512];
  enet_address_get_host_ip(&peer->host->address, chars, 512);
  std::string str(chars);
  delete[] chars;
  return str;
}

Port Peer::peerPort() {
  const ENetPeer *const peer = reinterpret_cast<const ENetPeer*>(this->peer);
  return peer->host->address.port;
}

Host::Host() :
  host(nullptr),
  rxBytes(0),
  txBytes(0) {
}

Host::~Host() {
  ENetHost *const host = reinterpret_cast<ENetHost*>(this->host);
  enet_host_destroy(host);
}

void Host::create(Port port, uint maxconn) {
  if (port == 0) { // Client
    host = enet_host_create(nullptr, 1, static_cast<size_t>(Channels::MAX), 0, 0);
  } else { // Server
    ENetAddress address;
    address.host = IN6ADDR_ANY_INIT; // ENET_HOST_ANY;
    address.port = port;
    host = enet_host_create(&address, maxconn, static_cast<size_t>(Channels::MAX), 0, 0);
  }
  if (host == nullptr) {
    throw Exception();
  }
}

Peer& Host::connect(const std::string &hostAddr, Port port, Timeout timeout) {
  ENetHost *const host = reinterpret_cast<ENetHost*>(this->host);
  ENetAddress address;
  ENetEvent event;
  ENetPeer *peer;

  enet_address_set_host(&address, hostAddr.c_str());
  address.port = port;

  peer = enet_host_connect(host, &address, static_cast<size_t>(Channels::MAX), 0);
  if (peer == nullptr) {
    throw Exception();
  }

  if (enet_host_service(host, &event, timeout) > 0 &&
    event.type == ENET_EVENT_TYPE_CONNECT) {
    Peer *p = new Peer(*this, peer);
    sendKeyExchange(*p);
    return *p;
  }

  enet_peer_reset(peer);
  throw Exception();
}

void Host::processPeersToDelete() {
  for (Peer *peer : m_peersToDelete) {
    delete peer;
  }
  m_peersToDelete.clear();
}

void Host::sendKeyExchange(Peer &p) {
  MsgTypes::ConnectionParamDHKeyExchange dhke;
  dhke.pk = p.connectionPk;
  OutMessage keMsg;
  dhke.writeToMsg(keMsg);
  send(p, keMsg);
}

/*static void hexDump(char in, uint8 *buf, int len) {
  std::cout << in << ": " << std::setiosflags(std::ios::internal);
  for (int i=0; i < len; ++i) {
    std::cout << std::setfill('0') << std::setw(2) << std::hex << (int)buf[i] << ' ';
  }
  std::cout << std::dec << std::endl;
}*/

bool Host::recv(InMessage &msg, Peer **peer, Timeout timeout) {
  ENetHost *const host = reinterpret_cast<ENetHost*>(this->host);
  processPeersToDelete();
  auto start = std::chrono::steady_clock::now();

  ENetEvent event;
  while (true) {
    auto now = std::chrono::steady_clock::now();
    enet_uint32 elapsed = static_cast<enet_uint32>(
      std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count());
    if (enet_host_service(host, &event, timeout - elapsed) > 0) {
      Peer *peerPtr = event.peer == nullptr ? nullptr :
        reinterpret_cast<Peer*>(event.peer->data);
      switch (event.type) {
      case ENET_EVENT_TYPE_NONE:
        break;
      case ENET_EVENT_TYPE_CONNECT:
        peerPtr = new Peer(*this, event.peer);
        *peer = peerPtr;
        sendKeyExchange(*peerPtr);
        msg.setType(MessageType::NetConnect);
        return true;
      case ENET_EVENT_TYPE_RECEIVE: {
        if (peer) {
          *peer = peerPtr;
        }

        const Message::SizeT pktLen = event.packet->dataLength;
        const Channels pktChannel = static_cast<Channels>(event.channelID);
        const bool decrypt = (pktChannel == Channels::ConnectionMetaPlain);
        byte *rcvData = new uint8[pktLen];
        if (decrypt) {
          // TODO: decryption
          std::memcpy(rcvData, event.packet->data, pktLen);
        } else {
          std::memcpy(rcvData, event.packet->data, pktLen);
        }
        // pktData's ownership is transferred to msg
        msg.fromData(rcvData, pktLen, pktChannel);
        rxBytes += event.packet->dataLength;

        using CPS = MsgTypes::ConnectionParamSubtype;
        if (msg.getType() == MessageType::ConnectionParam &&
            msg.getSubtype<CPS>() == CPS::DHKeyExchange) {
          MsgTypes::ConnectionParamDHKeyExchange dhke; dhke.readFromMsg(msg);
          peerPtr->remotePk = dhke.pk;
          if (Crypto::DiffieHellman::scalarmult(peerPtr->connectionSk, peerPtr->remotePk,
            peerPtr->sharedSecret) != 0) {
            // TODO: properly handle key exchange failure
            throw std::runtime_error("DH key exchange failed");
          }
          Log(Debug, TAG) << "hello DH! " << peerPtr->sharedSecret.hex();
        } else {
          return true;
        }
      } break;
      case ENET_EVENT_TYPE_DISCONNECT:
        if (peer) {
          *peer = peerPtr;
        }
        msg.setType(MessageType::NetDisconnect);
        m_peersToDelete.emplace_back(peerPtr);
        return true;
      }
    } else {
      return false;
    }
  }
  throw Exception();
}

void Host::send(Peer &peer, const OutMessage &msg, Tfer mode, Channels chan) {
  ENetHost *const host = reinterpret_cast<ENetHost*>(this->host);
  const bool encrypt = (chan == Channels::ConnectionMetaPlain);

  const byte header[Message::HeaderSize] = {
    static_cast<byte>(msg.m_type),
    msg.m_subtype
  };

  size_t pktLen = Message::HeaderSize + (msg.m_actualData == nullptr ? 0 : msg.m_length);
  ENetPacket *packet = enet_packet_create(nullptr, pktLen, TferToFlags(mode));
  byte *pktData = packet->data;
  txBytes += pktLen;
  if (msg.m_actualData != nullptr) {
    std::memcpy(msg.m_actualData, header, Message::HeaderSize);
    if (encrypt) {
      // TODO: don't memcpy, encrypt!
      std::memcpy(pktData, msg.m_actualData, pktLen);
    } else {
      std::memcpy(pktData, msg.m_actualData, pktLen);
    }
  } else {
    if (encrypt) {
      // TODO: don't memcpy, encrypt!
      std::memcpy(pktData, header, pktLen);
    } else {
      std::memcpy(pktData, header, pktLen);
    }
  }

  //hexDump('S', pktData, pktLen);
  enet_peer_send(reinterpret_cast<ENetPeer*>(peer.peer), static_cast<uint8>(chan), packet);
  enet_host_flush(host);
}

}
}
<commit_msg>Host::create: specify IPv6 scope ID (0)<commit_after>#include "Network.hpp"

#include <chrono>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <memory>

#include <enet/enet.h>

#include <meiose/msgpack.hpp>
#include <meiose/variant.hpp>

#include "../crypto/Random.hpp"
#include "../util/Log.hpp"
#include "msgtypes/ConnectionParam.hpp"

#include <iomanip>

struct membuf : std::streambuf {
  membuf(char* begin, char* end) {
    setg(begin, begin, end);
  }
  membuf(void* begin, void* end) :
    membuf(reinterpret_cast<char*>(begin), reinterpret_cast<char*>(end)) {}
  membuf(const void* begin, const void* end) :
    membuf(const_cast<void*>(begin), const_cast<void*>(end)) {}
};
class omsgbuf : public std::streambuf {
protected:
  diggler::net::OutMessage &omsg;
public:
  omsgbuf(diggler::net::OutMessage &o) : omsg(o) {}
protected:
  std::streamsize xsputn(const char_type* s, std::streamsize n) override {
    omsg.writeData(s, n);
    return n;
  }
  int_type overflow(int_type ch) override {
    omsg.writeI8(ch);
    return 1;
  }
};

namespace diggler {
namespace net {

using Util::Log;
using namespace Util::Logging::LogLevels;

static const char *TAG = "Network";

static bool InitDone = false;

bool Init() {
  if (InitDone)
    return true;
  InitDone = true;
  return enet_initialize() == 0;
}

void DeInit() {
  enet_deinitialize();
}

std::string GetNetworkLibVersion() {
  ENetVersion ver = enet_linked_version();
  std::ostringstream sstm;
  sstm << "ENet linked " << ENET_VERSION_MAJOR << '.' << ENET_VERSION_MINOR
    << '.' << ENET_VERSION_PATCH << ", using " << ENET_VERSION_GET_MAJOR(ver)
    << '.' << ENET_VERSION_GET_MINOR(ver) << '.' << ENET_VERSION_GET_PATCH(ver);
  return sstm.str();
}

static enet_uint32 TferToFlags(Tfer mode) {
  switch (mode) {
  case Tfer::Rel:
    return ENET_PACKET_FLAG_RELIABLE;
  case Tfer::Unseq:
    return ENET_PACKET_FLAG_UNSEQUENCED;
  case Tfer::Unrel:
    break;
  }
  return 0;
}

Message::Message(MessageType t, uint8 s) :
  MemoryStream(nullptr, 0),
  m_type(t),
  m_subtype(s) {
}

InMessage::InMessage() :
  Message(MessageType::Null, 0),
  m_chan(Channels::Base) {
}

InMessage::~InMessage() {
  free();
}

void InMessage::setType(MessageType type) {
  free();
  m_type = type;
  m_subtype = m_length = m_cursor = 0;
  m_data = nullptr;
}

void InMessage::fromData(const void *data, SizeT len, Channels chan) {
  if (len < HeaderSize) {
    throw std::invalid_argument("Message length is smaller than message header");
  }
  const uint8 *const bytes = static_cast<const uint8*>(data);
  free();
  m_chan = chan;
  m_cursor = 0;
  m_length = len - HeaderSize;
  m_type = static_cast<MessageType>(bytes[0]);
  m_subtype = bytes[1];
  // m_data/bytes is guaranteed never to be written to, so we can const_cast it
  m_data = const_cast<uint8*>(bytes) + HeaderSize;
}

void InMessage::free() {
  if (m_data != nullptr) {
    delete[] (m_data - HeaderSize);
  }
  m_type = MessageType::Null;
  m_subtype = m_length = m_cursor = 0;
  m_data = nullptr;
}

void InMessage::readMsgpack(meiose::variant &var) {
  uint32 len = readU32();
  if (len > remaining()) {
    throw std::runtime_error("Not enough bytes available for reported msgpack length");
  }
  const void *start = getCursorPtr(len);
  membuf sbuf(start, getCursorPtr());
  std::istream in(&sbuf);
  meiose::msgpack::read(in, var);
}

Channels InMessage::getChannel() const {
  return m_chan;
}

OutMessage::OutMessage(MessageType t, uint8 subtype) :
  Message(t, subtype),
  m_actualData(nullptr) {
}

OutMessage::~OutMessage() {
  std::free(m_actualData);
  m_data = nullptr;
}

const static int OutMessage_AllocStep = 1024;
void OutMessage::fit(SizeT len) {
  if (len <= m_allocated)
    return;
  SizeT targetSize = ((len + OutMessage_AllocStep - 1) /
    OutMessage_AllocStep)*OutMessage_AllocStep; // Round up
  using DataT = decltype(m_actualData);
  DataT newActualData = static_cast<DataT>(
    std::realloc(m_actualData, HeaderSize + targetSize));
  if (newActualData == nullptr)
    throw std::bad_alloc();
  m_actualData = newActualData;
  m_data = newActualData + HeaderSize;
  m_allocated = targetSize;
}

void OutMessage::writeMsgpack(const meiose::variant &var) {
  PosT pos = tell();
  writeU32(0);
  omsgbuf sbuf(*this);
  std::ostream out(&sbuf);
  meiose::msgpack::write(out, var);
  PosT posWritten = tell();
  seek(pos);
  writeU32(static_cast<uint32>(posWritten - (pos + sizeof(uint32))));
  seek(posWritten);
}

glm::vec3 InMessage::readVec3() {
  float x, y, z;
  x = readFloat();
  y = readFloat();
  z = readFloat();
  return glm::vec3(x, y, z);
}

glm::ivec3 InMessage::readIVec3() {
  int32 x, y, z;
  x = readI32();
  y = readI32();
  z = readI32();
  return glm::ivec3(x, y, z);
}

Peer::Peer(Host &host, void *peer) :
  host(host),
  peer(peer) {
  reinterpret_cast<ENetPeer*>(this->peer)->data = this;
  Crypto::Random::randomData(connectionPk);
  Crypto::DiffieHellman::scalarmultBase(connectionSk, connectionPk);
}

bool Peer::operator==(const Peer &other) const {
  return peer == other.peer;
}

bool Peer::operator!=(const Peer &other) const {
  return !(*this == other);
}

void Peer::disconnect(uint32 data) {
  ENetPeer *const peer = reinterpret_cast<ENetPeer*>(this->peer);
  enet_peer_disconnect(peer, data);
}

std::string Peer::peerHost() {
  const ENetPeer *const peer = reinterpret_cast<const ENetPeer*>(this->peer);
  std::ostringstream oss;
  char *chars = new char[512];
  enet_address_get_host_ip(&peer->host->address, chars, 512);
  oss << chars;
  delete[] chars;
  oss << ':' << peer->host->address.port;
  return oss.str();
}

std::string Peer::peerIP() {
  const ENetPeer *const peer = reinterpret_cast<const ENetPeer*>(this->peer);
  char *chars = new char[512];
  enet_address_get_host_ip(&peer->host->address, chars, 512);
  std::string str(chars);
  delete[] chars;
  return str;
}

Port Peer::peerPort() {
  const ENetPeer *const peer = reinterpret_cast<const ENetPeer*>(this->peer);
  return peer->host->address.port;
}

Host::Host() :
  host(nullptr),
  rxBytes(0),
  txBytes(0) {
}

Host::~Host() {
  ENetHost *const host = reinterpret_cast<ENetHost*>(this->host);
  enet_host_destroy(host);
}

void Host::create(Port port, uint maxconn) {
  if (port == 0) { // Client
    host = enet_host_create(nullptr, 1, static_cast<size_t>(Channels::MAX), 0, 0);
  } else { // Server
    ENetAddress address;
    address.host = in6addr_any;
    address.sin6_scope_id = 0;
    address.port = port;
    host = enet_host_create(&address, maxconn, static_cast<size_t>(Channels::MAX), 0, 0);
  }
  if (host == nullptr) {
    throw Exception();
  }
}

Peer& Host::connect(const std::string &hostAddr, Port port, Timeout timeout) {
  ENetHost *const host = reinterpret_cast<ENetHost*>(this->host);
  ENetAddress address;
  ENetEvent event;
  ENetPeer *peer;

  enet_address_set_host(&address, hostAddr.c_str());
  address.port = port;

  peer = enet_host_connect(host, &address, static_cast<size_t>(Channels::MAX), 0);
  if (peer == nullptr) {
    throw Exception();
  }

  if (enet_host_service(host, &event, timeout) > 0 &&
    event.type == ENET_EVENT_TYPE_CONNECT) {
    Peer *p = new Peer(*this, peer);
    sendKeyExchange(*p);
    return *p;
  }

  enet_peer_reset(peer);
  throw Exception();
}

void Host::processPeersToDelete() {
  for (Peer *peer : m_peersToDelete) {
    delete peer;
  }
  m_peersToDelete.clear();
}

void Host::sendKeyExchange(Peer &p) {
  MsgTypes::ConnectionParamDHKeyExchange dhke;
  dhke.pk = p.connectionPk;
  OutMessage keMsg;
  dhke.writeToMsg(keMsg);
  send(p, keMsg);
}

/*static void hexDump(char in, uint8 *buf, int len) {
  std::cout << in << ": " << std::setiosflags(std::ios::internal);
  for (int i=0; i < len; ++i) {
    std::cout << std::setfill('0') << std::setw(2) << std::hex << (int)buf[i] << ' ';
  }
  std::cout << std::dec << std::endl;
}*/

bool Host::recv(InMessage &msg, Peer **peer, Timeout timeout) {
  ENetHost *const host = reinterpret_cast<ENetHost*>(this->host);
  processPeersToDelete();
  auto start = std::chrono::steady_clock::now();

  ENetEvent event;
  while (true) {
    auto now = std::chrono::steady_clock::now();
    enet_uint32 elapsed = static_cast<enet_uint32>(
      std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count());
    if (enet_host_service(host, &event, timeout - elapsed) > 0) {
      Peer *peerPtr = event.peer == nullptr ? nullptr :
        reinterpret_cast<Peer*>(event.peer->data);
      switch (event.type) {
      case ENET_EVENT_TYPE_NONE:
        break;
      case ENET_EVENT_TYPE_CONNECT:
        peerPtr = new Peer(*this, event.peer);
        *peer = peerPtr;
        sendKeyExchange(*peerPtr);
        msg.setType(MessageType::NetConnect);
        return true;
      case ENET_EVENT_TYPE_RECEIVE: {
        if (peer) {
          *peer = peerPtr;
        }

        const Message::SizeT pktLen = event.packet->dataLength;
        const Channels pktChannel = static_cast<Channels>(event.channelID);
        const bool decrypt = (pktChannel == Channels::ConnectionMetaPlain);
        byte *rcvData = new uint8[pktLen];
        if (decrypt) {
          // TODO: decryption
          std::memcpy(rcvData, event.packet->data, pktLen);
        } else {
          std::memcpy(rcvData, event.packet->data, pktLen);
        }
        // pktData's ownership is transferred to msg
        msg.fromData(rcvData, pktLen, pktChannel);
        rxBytes += event.packet->dataLength;

        using CPS = MsgTypes::ConnectionParamSubtype;
        if (msg.getType() == MessageType::ConnectionParam &&
            msg.getSubtype<CPS>() == CPS::DHKeyExchange) {
          MsgTypes::ConnectionParamDHKeyExchange dhke; dhke.readFromMsg(msg);
          peerPtr->remotePk = dhke.pk;
          if (Crypto::DiffieHellman::scalarmult(peerPtr->connectionSk, peerPtr->remotePk,
            peerPtr->sharedSecret) != 0) {
            // TODO: properly handle key exchange failure
            throw std::runtime_error("DH key exchange failed");
          }
          Log(Debug, TAG) << "hello DH! " << peerPtr->sharedSecret.hex();
        } else {
          return true;
        }
      } break;
      case ENET_EVENT_TYPE_DISCONNECT:
        if (peer) {
          *peer = peerPtr;
        }
        msg.setType(MessageType::NetDisconnect);
        m_peersToDelete.emplace_back(peerPtr);
        return true;
      }
    } else {
      return false;
    }
  }
  throw Exception();
}

void Host::send(Peer &peer, const OutMessage &msg, Tfer mode, Channels chan) {
  ENetHost *const host = reinterpret_cast<ENetHost*>(this->host);
  const bool encrypt = (chan == Channels::ConnectionMetaPlain);

  const byte header[Message::HeaderSize] = {
    static_cast<byte>(msg.m_type),
    msg.m_subtype
  };

  size_t pktLen = Message::HeaderSize + (msg.m_actualData == nullptr ? 0 : msg.m_length);
  ENetPacket *packet = enet_packet_create(nullptr, pktLen, TferToFlags(mode));
  byte *pktData = packet->data;
  txBytes += pktLen;
  if (msg.m_actualData != nullptr) {
    std::memcpy(msg.m_actualData, header, Message::HeaderSize);
    if (encrypt) {
      // TODO: don't memcpy, encrypt!
      std::memcpy(pktData, msg.m_actualData, pktLen);
    } else {
      std::memcpy(pktData, msg.m_actualData, pktLen);
    }
  } else {
    if (encrypt) {
      // TODO: don't memcpy, encrypt!
      std::memcpy(pktData, header, pktLen);
    } else {
      std::memcpy(pktData, header, pktLen);
    }
  }

  //hexDump('S', pktData, pktLen);
  enet_peer_send(reinterpret_cast<ENetPeer*>(peer.peer), static_cast<uint8>(chan), packet);
  enet_host_flush(host);
}

}
}
<|endoftext|>


<commit_before>#include "EclipseSourceCodeAccessPrivatePCH.h"
#include "EclipseSourceCodeAccessor.h"
#include "ModuleManager.h"
#include "DesktopPlatformModule.h"

#if WITH_EDITOR
#include "Developer/HotReload/Public/IHotReload.h"
#endif

DEFINE_LOG_CATEGORY_STATIC(LogEclipseAccessor, Log, All);

#define LOCTEXT_NAMESPACE "EclipseSourceCodeAccessor"

// http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fruntime-options.html

bool FEclipseSourceCodeAccessor::CanAccessSourceCode() const
{
	return true;
}

FName FEclipseSourceCodeAccessor::GetFName() const
{
	return FName("EclipseSourceCodeAccessor");
}

FText FEclipseSourceCodeAccessor::GetNameText() const
{
	return LOCTEXT("EclipseDisplayName", "Eclipse");
}

FText FEclipseSourceCodeAccessor::GetDescriptionText() const
{
	return LOCTEXT("EclipseDisplayDesc", "Open source code files with Eclipse");
}

bool FEclipseSourceCodeAccessor::OpenSolution()
{
	FString Filename = FPaths::GetBaseFilename(GetSolutionPath()) + ".cproject";
	FString Directory = FPaths::GetPath(GetSolutionPath());
	FString Solution = "\"" + Directory + "/" + Filename + "\"";
	FString EclipsePath;
	if (!CanRunEclipse(EclipsePath))
	{
		return false;
	}

	UE_LOG(LogEclipseAccessor, Warning, TEXT("FEclipseSourceCodeAccessor::OpenSolution: %s %s"), *EclipsePath, *Solution);

	FProcHandle Proc = FPlatformProcess::CreateProc(*EclipsePath, *Solution, true, false, false, nullptr, 0, nullptr, nullptr);
	if(Proc.IsValid())
	{
		FPlatformProcess::CloseProc(Proc);
		return true;
	}
	return false;
}

bool FEclipseSourceCodeAccessor::OpenFileAtLine(const FString& FullPath, int32 LineNumber, int32 ColumnNumber)
{
	const FString FileWithLine = FString::Printf(TEXT("%s:%i "), *FullPath, LineNumber);
	TArray<FString> Files(FileWithLine);
	return OpensourceFiles(Files);
}

bool FEclipseSourceCodeAccessor::OpenSourceFiles(const TArray<FString>& AbsoluteSourcePaths)
{
	FString EclipsePath;
	if (!CanRunEclipse(EclipsePath))
	{
		return false;
	}
	FString Args = FString(TEXT("--launcher.timeout 60 --launcher.openFile "));
	for (const FString& SourcePath : AbsoluteSourcePaths)
	{
		const FString NewSourcePath = FString::Printf(TEXT("\"%s\" "), *SourcePath);
		Args.Append(NewSourcePath);
	}
	FProcHandle Proc = FPlatformProcess::CreateProc(*EclipsePath, *Solution, true, false, false, nullptr, 0, nullptr, nullptr);
	if(Proc.IsValid())
	{
		FPlatformProcess::CloseProc(Proc);
		return true;
	}
	return false;
}

bool FEclipseSourceCodeAccessor::AddSourceFiles(const TArray<FString>& AbsoluteSourcePaths, const TArray<FString>& AvailableModules)
{
	return false;
}

bool FEclipseSourceCodeAccessor::SaveAllOpenDocuments() const
{
	return false;
}

void FEclipseSourceCodeAccessor::Tick(const float DeltaTime)
{
}

void FEclispeSourceCodeAccessor::CanRunEclipse(FString& OutPath) const
{
	// TODO This might be not a good idea to find an executable.
	OutPath = TEXT("/usr/bin/eclipse");
	if (!FPaths::FileExists(OutPath))
	{
		TCHAR EclipseBinaryEnv[32768] = { 0 };
		FPlatformMisc::GetEnvironmentVariable(TEXT("UE4_ECLIPSE_BINARY"), EclipseBinaryEnv, ARRAY_COUNT(EclipseBinaryEnv));
		OutPath = EclipseBinaryEnv;

		if (!FPaths::FileExists(OutPath))
		{
			UE_LOG(LogEclipseAccessor, Warning, TEXT("FEclipseSourceCodeAccessor: Can not find eclipse binary - export UE4_ECLIPSE_BINARY environment variable"));
			return false;
		}
	}
	return true;
}

FString FEclipseSourceCodeAccessor::GetSolutionPath() const
{
	if (IsInGameThread())
	{
		FString SolutionPath;
		if (FDesktopPlatformModule::Get()->GetSolutionPath(SolutionPath))
		{
			CachedSolutionPath = FPaths::ConvertRelativePathToFull(SolutionPath);
		}
	}
	return CachedSolutionPath;
}

void FEclipseSourceCodeAccessor::Startup()
{
	// Cache this so we don't have to do it on a background thread
	GetSolutionPath();
}

void FEclipseSourceCodeAccessor::Shutdown()
{
}
<commit_msg>fixed TArray usage<commit_after>#include "EclipseSourceCodeAccessPrivatePCH.h"
#include "EclipseSourceCodeAccessor.h"
#include "ModuleManager.h"
#include "DesktopPlatformModule.h"

#if WITH_EDITOR
#include "Developer/HotReload/Public/IHotReload.h"
#endif

DEFINE_LOG_CATEGORY_STATIC(LogEclipseAccessor, Log, All);

#define LOCTEXT_NAMESPACE "EclipseSourceCodeAccessor"

// http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Fruntime-options.html

bool FEclipseSourceCodeAccessor::CanAccessSourceCode() const
{
	return true;
}

FName FEclipseSourceCodeAccessor::GetFName() const
{
	return FName("EclipseSourceCodeAccessor");
}

FText FEclipseSourceCodeAccessor::GetNameText() const
{
	return LOCTEXT("EclipseDisplayName", "Eclipse");
}

FText FEclipseSourceCodeAccessor::GetDescriptionText() const
{
	return LOCTEXT("EclipseDisplayDesc", "Open source code files with Eclipse");
}

bool FEclipseSourceCodeAccessor::OpenSolution()
{
	FString Filename = FPaths::GetBaseFilename(GetSolutionPath()) + ".cproject";
	FString Directory = FPaths::GetPath(GetSolutionPath());
	FString Solution = "\"" + Directory + "/" + Filename + "\"";
	FString EclipsePath;
	if (!CanRunEclipse(EclipsePath))
	{
		return false;
	}

	UE_LOG(LogEclipseAccessor, Warning, TEXT("FEclipseSourceCodeAccessor::OpenSolution: %s %s"), *EclipsePath, *Solution);

	FProcHandle Proc = FPlatformProcess::CreateProc(*EclipsePath, *Solution, true, false, false, nullptr, 0, nullptr, nullptr);
	if(Proc.IsValid())
	{
		FPlatformProcess::CloseProc(Proc);
		return true;
	}
	return false;
}

bool FEclipseSourceCodeAccessor::OpenFileAtLine(const FString& FullPath, int32 LineNumber, int32 ColumnNumber)
{
	TArray<FString> Files;
	Files.Emplace(FString::Printf(TEXT("%s:%i "), *FullPath, LineNumber));
	return OpensourceFiles(Files);
}

bool FEclipseSourceCodeAccessor::OpenSourceFiles(const TArray<FString>& AbsoluteSourcePaths)
{
	FString EclipsePath;
	if (!CanRunEclipse(EclipsePath))
	{
		return false;
	}
	FString Args = FString(TEXT("--launcher.timeout 60 --launcher.openFile "));
	for (const FString& SourcePath : AbsoluteSourcePaths)
	{
		const FString NewSourcePath = FString::Printf(TEXT("\"%s\" "), *SourcePath);
		Args.Append(NewSourcePath);
	}
	FProcHandle Proc = FPlatformProcess::CreateProc(*EclipsePath, *Solution, true, false, false, nullptr, 0, nullptr, nullptr);
	if(Proc.IsValid())
	{
		FPlatformProcess::CloseProc(Proc);
		return true;
	}
	return false;
}

bool FEclipseSourceCodeAccessor::AddSourceFiles(const TArray<FString>& AbsoluteSourcePaths, const TArray<FString>& AvailableModules)
{
	return false;
}

bool FEclipseSourceCodeAccessor::SaveAllOpenDocuments() const
{
	return false;
}

void FEclipseSourceCodeAccessor::Tick(const float DeltaTime)
{
}

void FEclispeSourceCodeAccessor::CanRunEclipse(FString& OutPath) const
{
	// TODO This might be not a good idea to find an executable.
	OutPath = TEXT("/usr/bin/eclipse");
	if (!FPaths::FileExists(OutPath))
	{
		TCHAR EclipseBinaryEnv[32768] = { 0 };
		FPlatformMisc::GetEnvironmentVariable(TEXT("UE4_ECLIPSE_BINARY"), EclipseBinaryEnv, ARRAY_COUNT(EclipseBinaryEnv));
		OutPath = EclipseBinaryEnv;

		if (!FPaths::FileExists(OutPath))
		{
			UE_LOG(LogEclipseAccessor, Warning, TEXT("FEclipseSourceCodeAccessor: Can not find eclipse binary - export UE4_ECLIPSE_BINARY environment variable"));
			return false;
		}
	}
	return true;
}

FString FEclipseSourceCodeAccessor::GetSolutionPath() const
{
	if (IsInGameThread())
	{
		FString SolutionPath;
		if (FDesktopPlatformModule::Get()->GetSolutionPath(SolutionPath))
		{
			CachedSolutionPath = FPaths::ConvertRelativePathToFull(SolutionPath);
		}
	}
	return CachedSolutionPath;
}

void FEclipseSourceCodeAccessor::Startup()
{
	// Cache this so we don't have to do it on a background thread
	GetSolutionPath();
}

void FEclipseSourceCodeAccessor::Shutdown()
{
}
<|endoftext|>