<commit_before>//===------- MandatoryCombiner.cpp ----------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
///  \file
///
///  Defines the MandatoryCombiner function transform.  The pass contains basic
///  instruction combines to be performed at the begining of both the Onone and
///  also the performance pass pipelines, after the diagnostics passes have been
///  run.  It is intended to be run before and to be independent of other
///  transforms.
///
///  The intention of this pass is to be a place for mandatory peepholes that
///  are not needed for diagnostics. Please put any such peepholes here instead
///  of in the diagnostic passes.
///
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-mandatory-combiner"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SIL/SILInstructionWorklist.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace swift;

namespace {

class MandatoryCombiner final
    : public SILInstructionVisitor<MandatoryCombiner, SILInstruction *> {

  using Worklist = SmallSILInstructionWorklist<256>;

  /// The list of instructions remaining to visit, perhaps to combine.
  Worklist worklist;

  /// Whether any changes have been made.
  bool madeChange;

  /// The number of times that the worklist has been processed.
  unsigned iteration;

  InstModCallbacks instModCallbacks;
  SmallVectorImpl<SILInstruction *> &createdInstructions;
  SmallVector<SILInstruction *, 16> instructionsPendingDeletion;

public:
  MandatoryCombiner(
      SmallVectorImpl<SILInstruction *> &createdInstructions)
      : worklist("MC"), madeChange(false), iteration(0),
        instModCallbacks(
            [&](SILInstruction *instruction) {
              worklist.erase(instruction);
              instructionsPendingDeletion.push_back(instruction);
            },
            [&](SILInstruction *instruction) { worklist.add(instruction); }),
        createdInstructions(createdInstructions){};

  /// Base visitor that does not do anything.
  SILInstruction *visitSILInstruction(SILInstruction *) { return nullptr; }

  /// \returns whether all the values are of trivial type in the provided
  ///          function.
  template <typename Values>
  static bool areAllValuesTrivial(Values values, SILFunction &function) {
    return llvm::all_of(values, [&](SILValue value) -> bool {
      return value->getType().isTrivial(function);
    });
  }

  SILInstruction *visitApplyInst(ApplyInst *instruction) {
    // Apply this pass only to partial applies all of whose arguments are
    // trivial.
    auto calledValue = instruction->getCallee();
    if (calledValue == nullptr) {
      return nullptr;
    }
    auto fullApplyCallee = calledValue->getDefiningInstruction();
    if (fullApplyCallee == nullptr) {
      return nullptr;
    }
    auto partialApply = dyn_cast<PartialApplyInst>(fullApplyCallee);
    if (partialApply == nullptr) {
      return nullptr;
    }
    auto *function = partialApply->getCalleeFunction();
    if (function == nullptr) {
      return nullptr;
    }
    ApplySite fullApplySite(instruction);
    auto fullApplyArguments = fullApplySite.getArguments();
    if (!areAllValuesTrivial(fullApplyArguments, *function)) {
      return nullptr;
    }
    auto partialApplyArguments = ApplySite(partialApply).getArguments();
    if (!areAllValuesTrivial(partialApplyArguments, *function)) {
      return nullptr;
    }

    auto callee = partialApply->getCallee();

    ApplySite partialApplySite(partialApply);

    SmallVector<SILValue, 8> argsVec;
    llvm::copy(fullApplyArguments, std::back_inserter(argsVec));
    llvm::copy(partialApplyArguments, std::back_inserter(argsVec));

    SILBuilderWithScope builder(instruction, &createdInstructions);
    ApplyInst *replacement = builder.createApply(
        /*Loc=*/instruction->getDebugLocation().getLocation(), /*Fn=*/callee,
        /*Subs=*/partialApply->getSubstitutionMap(),
        /*Args*/ argsVec,
        /*isNonThrowing=*/instruction->isNonThrowing(),
        /*SpecializationInfo=*/partialApply->getSpecializationInfo());

    worklist.replaceInstructionWithInstruction(instruction, replacement
#ifndef NDEBUG
                                               ,
                                               /*instructionDescription=*/""
#endif
    );
    tryDeleteDeadClosure(partialApply, instModCallbacks);
    return nullptr;
  }

  void addReachableCodeToWorklist(SILFunction &function) {
    SmallBlotSetVector<SILBasicBlock *, 32> blockWorklist;
    SmallBlotSetVector<SILBasicBlock *, 32> blocksVisited;
    SmallVector<SILInstruction *, 128> instructions;

    blockWorklist.insert(&*function.begin());
    while (!blockWorklist.empty()) {
      auto *block = blockWorklist.pop_back_val().getValueOr(nullptr);
      if (block == nullptr) {
        continue;
      }

      if (!blocksVisited.insert(block).second) {
        continue;
      }

      for (auto iterator = block->begin(), end = block->end(); iterator != end;) {
        auto *instruction = &*iterator;
        ++iterator;

        if (isInstructionTriviallyDead(instruction)) {
          continue;
        }

        instructions.push_back(instruction);
      }

      for_each(block->getSuccessorBlocks(), [&](SILBasicBlock *block) {
        blockWorklist.insert(block);
      });
    }

    worklist.addInitialGroup(instructions);
  }

  /// \return whether a change was made.
  bool doOneIteration(SILFunction &function, unsigned iteration) {
    madeChange = false;

    addReachableCodeToWorklist(function);

    while (!worklist.isEmpty()) {
      auto *instruction = worklist.pop_back_val();
      if (instruction == nullptr) {
        continue;
      }

#ifndef NDEBUG
      std::string instructionDescription;
#endif
      LLVM_DEBUG(llvm::raw_string_ostream SS(instructionDescription);
                 instruction->print(SS); instructionDescription = SS.str(););
      LLVM_DEBUG(llvm::dbgs()
                 << "MC: Visiting: " << instructionDescription << '\n');

      if (auto replacement = visit(instruction)) {
        worklist.replaceInstructionWithInstruction(instruction, replacement
#ifndef NDEBUG
            , instructionDescription
#endif
          );
      }

      for (SILInstruction *instruction : instructionsPendingDeletion) {
        worklist.eraseInstFromFunction(*instruction);
      }
      instructionsPendingDeletion.clear();

      // Our tracking list has been accumulating instructions created by the
      // SILBuilder during this iteration. Go through the tracking list and add
      // its contents to the worklist and then clear said list in preparation
      // for the next iteration.
      for (SILInstruction *instruction : createdInstructions) {
        LLVM_DEBUG(llvm::dbgs() << "MC: add " << *instruction
                                << " from tracking list to worklist\n");
        worklist.add(instruction);
      }
      createdInstructions.clear();
    }

    worklist.resetChecked();
    return madeChange;
  }

  void clear() {
    iteration = 0;
    worklist.resetChecked();
    madeChange = false;
  }

  /// Applies the MandatoryCombiner to the provided function.
  ///
  /// \param function the function to which to apply the MandatoryCombiner.
  ///
  /// \return whether a change was made.
  bool runOnFunction(SILFunction &function) {
    bool changed = false;

    while (doOneIteration(function, iteration)) {
      changed = true;
      ++iteration;
    }

    return changed;
  }
};

} // end anonymous namespace

//===----------------------------------------------------------------------===//
//                            Top Level Entrypoint
//===----------------------------------------------------------------------===//

namespace {

class MandatoryCombine final : public SILFunctionTransform {

  SmallVector<SILInstruction *, 64> createdInstructions;

  void run() override {
    auto *function = getFunction();

    MandatoryCombiner combiner(createdInstructions);
    bool madeChange = combiner.runOnFunction(*function);

    if (madeChange) {
      invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
    }
  }

  void handleDeleteNotification(SILNode *node) override {
    // Remove instructions that were both created and deleted from the list of
    // created instructions which will eventually be added to the worklist.

    auto *instruction = dyn_cast<SILInstruction>(node);
    if (instruction == nullptr) {
      return;
    }

    // Linear searching the tracking list doesn't hurt because usually it only
    // contains a few elements.
    auto iterator = find(createdInstructions, instruction);
    if (createdInstructions.end() != iterator) {
      createdInstructions.erase(iterator);
    }
  }

  bool needsNotifications() override { return true; }
};

} // end anonymous namespace

SILTransform *swift::createMandatoryCombine() { return new MandatoryCombine(); }
<commit_msg>[mandatory-combiner] Some small cleanups.<commit_after>//===------- MandatoryCombiner.cpp ----------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
///  \file
///
///  Defines the MandatoryCombiner function transform.  The pass contains basic
///  instruction combines to be performed at the begining of both the Onone and
///  also the performance pass pipelines, after the diagnostics passes have been
///  run.  It is intended to be run before and to be independent of other
///  transforms.
///
///  The intention of this pass is to be a place for mandatory peepholes that
///  are not needed for diagnostics. Please put any such peepholes here instead
///  of in the diagnostic passes.
///
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-mandatory-combiner"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SIL/SILInstructionWorklist.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace swift;

//===----------------------------------------------------------------------===//
//                                  Utility
//===----------------------------------------------------------------------===//

/// \returns whether all the values are of trivial type in the provided
///          function.
template <typename Values>
static bool areAllValuesTrivial(Values values, SILFunction &function) {
  return llvm::all_of(values, [&](SILValue value) -> bool {
    return value->getType().isTrivial(function);
  });
}

//===----------------------------------------------------------------------===//
//                        MandatoryCombiner Interface
//===----------------------------------------------------------------------===//

namespace {

class MandatoryCombiner final
    : public SILInstructionVisitor<MandatoryCombiner, SILInstruction *> {

  using Worklist = SmallSILInstructionWorklist<256>;

  /// The list of instructions remaining to visit, perhaps to combine.
  Worklist worklist;

  /// Whether any changes have been made.
  bool madeChange;

  /// The number of times that the worklist has been processed.
  unsigned iteration;

  InstModCallbacks instModCallbacks;
  SmallVectorImpl<SILInstruction *> &createdInstructions;
  SmallVector<SILInstruction *, 16> instructionsPendingDeletion;

public:
  MandatoryCombiner(
      SmallVectorImpl<SILInstruction *> &createdInstructions)
      : worklist("MC"), madeChange(false), iteration(0),
        instModCallbacks(
            [&](SILInstruction *instruction) {
              worklist.erase(instruction);
              instructionsPendingDeletion.push_back(instruction);
            },
            [&](SILInstruction *instruction) { worklist.add(instruction); }),
        createdInstructions(createdInstructions){};

  void addReachableCodeToWorklist(SILFunction &function);

  /// \return whether a change was made.
  bool doOneIteration(SILFunction &function, unsigned iteration);

  void clear() {
    iteration = 0;
    worklist.resetChecked();
    madeChange = false;
  }

  /// Applies the MandatoryCombiner to the provided function.
  ///
  /// \param function the function to which to apply the MandatoryCombiner.
  ///
  /// \return whether a change was made.
  bool runOnFunction(SILFunction &function) {
    bool changed = false;

    while (doOneIteration(function, iteration)) {
      changed = true;
      ++iteration;
    }

    return changed;
  }

  /// Base visitor that does not do anything.
  SILInstruction *visitSILInstruction(SILInstruction *) { return nullptr; }
  SILInstruction *visitApplyInst(ApplyInst *instruction);
};

} // end anonymous namespace

//===----------------------------------------------------------------------===//
//               MandatoryCombiner Non-Visitor Utility Methods
//===----------------------------------------------------------------------===//

void MandatoryCombiner::addReachableCodeToWorklist(SILFunction &function) {
  SmallBlotSetVector<SILBasicBlock *, 32> blockWorklist;
  SmallBlotSetVector<SILBasicBlock *, 32> blocksVisited;
  SmallVector<SILInstruction *, 128> instructions;

  blockWorklist.insert(&*function.begin());
  while (!blockWorklist.empty()) {
    auto *block = blockWorklist.pop_back_val().getValueOr(nullptr);
    if (block == nullptr) {
      continue;
    }

    if (!blocksVisited.insert(block).second) {
      continue;
    }

    for (auto iterator = block->begin(), end = block->end(); iterator != end;) {
      auto *instruction = &*iterator;
      ++iterator;

      if (isInstructionTriviallyDead(instruction)) {
        continue;
      }

      instructions.push_back(instruction);
    }

    for_each(block->getSuccessorBlocks(),
             [&](SILBasicBlock *block) { blockWorklist.insert(block); });
  }

  worklist.addInitialGroup(instructions);
}

bool MandatoryCombiner::doOneIteration(SILFunction &function,
                                       unsigned iteration) {
  madeChange = false;

  addReachableCodeToWorklist(function);

  while (!worklist.isEmpty()) {
    auto *instruction = worklist.pop_back_val();
    if (instruction == nullptr) {
      continue;
    }

#ifndef NDEBUG
    std::string instructionDescription;
#endif
    LLVM_DEBUG(llvm::raw_string_ostream SS(instructionDescription);
               instruction->print(SS); instructionDescription = SS.str(););
    LLVM_DEBUG(llvm::dbgs()
               << "MC: Visiting: " << instructionDescription << '\n');

    if (auto replacement = visit(instruction)) {
      worklist.replaceInstructionWithInstruction(instruction, replacement
#ifndef NDEBUG
                                                 ,
                                                 instructionDescription
#endif
      );
    }

    for (SILInstruction *instruction : instructionsPendingDeletion) {
      worklist.eraseInstFromFunction(*instruction);
    }
    instructionsPendingDeletion.clear();

    // Our tracking list has been accumulating instructions created by the
    // SILBuilder during this iteration. Go through the tracking list and add
    // its contents to the worklist and then clear said list in preparation
    // for the next iteration.
    for (SILInstruction *instruction : createdInstructions) {
      LLVM_DEBUG(llvm::dbgs() << "MC: add " << *instruction
                              << " from tracking list to worklist\n");
      worklist.add(instruction);
    }
    createdInstructions.clear();
  }

  worklist.resetChecked();
  return madeChange;
}

//===----------------------------------------------------------------------===//
//                     MandatoryCombiner Visitor Methods
//===----------------------------------------------------------------------===//

SILInstruction *MandatoryCombiner::visitApplyInst(ApplyInst *instruction) {
  // Apply this pass only to partial applies all of whose arguments are
  // trivial.
  auto calledValue = instruction->getCallee();
  if (calledValue == nullptr) {
    return nullptr;
  }
  auto fullApplyCallee = calledValue->getDefiningInstruction();
  if (fullApplyCallee == nullptr) {
    return nullptr;
  }
  auto partialApply = dyn_cast<PartialApplyInst>(fullApplyCallee);
  if (partialApply == nullptr) {
    return nullptr;
  }
  auto *function = partialApply->getCalleeFunction();
  if (function == nullptr) {
    return nullptr;
  }
  ApplySite fullApplySite(instruction);
  auto fullApplyArguments = fullApplySite.getArguments();
  if (!areAllValuesTrivial(fullApplyArguments, *function)) {
    return nullptr;
  }
  auto partialApplyArguments = ApplySite(partialApply).getArguments();
  if (!areAllValuesTrivial(partialApplyArguments, *function)) {
    return nullptr;
  }

  auto callee = partialApply->getCallee();

  ApplySite partialApplySite(partialApply);

  SmallVector<SILValue, 8> argsVec;
  llvm::copy(fullApplyArguments, std::back_inserter(argsVec));
  llvm::copy(partialApplyArguments, std::back_inserter(argsVec));

  SILBuilderWithScope builder(instruction, &createdInstructions);
  ApplyInst *replacement = builder.createApply(
      /*Loc=*/instruction->getDebugLocation().getLocation(), /*Fn=*/callee,
      /*Subs=*/partialApply->getSubstitutionMap(),
      /*Args*/ argsVec,
      /*isNonThrowing=*/instruction->isNonThrowing(),
      /*SpecializationInfo=*/partialApply->getSpecializationInfo());

  worklist.replaceInstructionWithInstruction(instruction, replacement
#ifndef NDEBUG
                                             ,
                                             /*instructionDescription=*/""
#endif
  );
  tryDeleteDeadClosure(partialApply, instModCallbacks);
  return nullptr;
}

//===----------------------------------------------------------------------===//
//                            Top Level Entrypoint
//===----------------------------------------------------------------------===//

namespace {

class MandatoryCombine final : public SILFunctionTransform {

  SmallVector<SILInstruction *, 64> createdInstructions;

  void run() override {
    auto *function = getFunction();

    MandatoryCombiner combiner(createdInstructions);
    bool madeChange = combiner.runOnFunction(*function);

    if (madeChange) {
      invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
    }
  }

  void handleDeleteNotification(SILNode *node) override {
    // Remove instructions that were both created and deleted from the list of
    // created instructions which will eventually be added to the worklist.

    auto *instruction = dyn_cast<SILInstruction>(node);
    if (instruction == nullptr) {
      return;
    }

    // Linear searching the tracking list doesn't hurt because usually it only
    // contains a few elements.
    auto iterator = find(createdInstructions, instruction);
    if (createdInstructions.end() != iterator) {
      createdInstructions.erase(iterator);
    }
  }

  bool needsNotifications() override { return true; }
};

} // end anonymous namespace

SILTransform *swift::createMandatoryCombine() { return new MandatoryCombine(); }
<|endoftext|>


<commit_before>#pragma once

#include <concepts>
#include <cstddef>
#include <filesystem>
#include <phosphor-logging/lg2/flags.hpp>
#include <phosphor-logging/lg2/header.hpp>
#include <phosphor-logging/lg2/level.hpp>
#include <phosphor-logging/lg2/logger.hpp>
#include <phosphor-logging/lg2/source_location.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <string_view>
#include <tuple>
#include <type_traits>

namespace lg2::details
{

/** Concept to determine if an item acts like a string.
 *
 *  Something acts like a string if we can construct a string_view from it.
 *  This covers std::string and C-strings.  But, there is subtlety in that
 *  nullptr_t can be used to construct a string_view until C++23, so we need
 *  to exempt out nullptr_t's (otherwise `nullptr` ends up acting like a
 *  string).
 */
template <typename T>
concept string_like_type =
    (std::constructible_from<std::string_view, T> ||
     std::same_as<std::filesystem::path,
                  std::decay_t<T>>)&&!std::same_as<nullptr_t, T>;

/** Concept to determine if an item acts like a pointer.
 *
 *  Any pointer, which doesn't act like a string, should be treated as a raw
 *  pointer.
 */
template <typename T>
concept pointer_type = (std::is_pointer_v<T> ||
                        std::same_as<nullptr_t, T>)&&!string_like_type<T>;

/** Concept to determine if an item acts like an unsigned_integral.
 *
 *  Except bool because we want bool to be handled special to create nice
 *  `True` and `False` strings.
 */
template <typename T>
concept unsigned_integral_except_bool =
    !std::same_as<T, bool> && std::unsigned_integral<T>;

template <typename T>
concept sdbusplus_enum = sdbusplus::message::has_convert_from_string_v<T>;

template <typename T>
concept exception_type = std::derived_from<std::decay_t<T>, std::exception>;

/** Concept listing all of the types we know how to convert into a format
 *  for logging.
 */
template <typename T>
concept unsupported_log_convert_types =
    !(unsigned_integral_except_bool<T> || std::signed_integral<T> ||
      std::same_as<bool, T> || std::floating_point<T> || string_like_type<T> ||
      pointer_type<T> || sdbusplus_enum<T> || exception_type<T>);

/** Any type we do not know how to convert for logging gives a nicer
 *  static_assert message. */
template <log_flags... Fs, unsupported_log_convert_types T>
static auto log_convert(const char*, log_flag<Fs...>, T)
{
    static_assert(!std::is_same_v<T, T>, "Unsupported type for logging value.");
    // Having this return of an empty tuple reduces the error messages.
    return std::tuple<>{};
}

/** Logging conversion for unsigned. */
template <log_flags... Fs, unsigned_integral_except_bool V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, floating);
    prohibit(f, signed_val);
    prohibit(f, str);

    one_from_set(f, dec | hex | bin);
    one_from_set(f, field8 | field16 | field32 | field64);

    // Add 'unsigned' flag, force to uint64_t for variadic passing.
    return std::make_tuple(h, (f | unsigned_val).value,
                           static_cast<uint64_t>(v));
}

/** Logging conversion for signed. */
template <log_flags... Fs, std::signed_integral V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, floating);
    prohibit(f, str);
    prohibit(f, unsigned_val);

    one_from_set(f, dec | hex | bin);
    one_from_set(f, field8 | field16 | field32 | field64);

    // Add 'signed' flag, force to int64_t for variadic passing.
    return std::make_tuple(h, (f | signed_val).value, static_cast<int64_t>(v));
}

/** Logging conversion for bool. */
template <log_flags... Fs, std::same_as<bool> V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Cast bools to a "True" or "False" string.
    return std::make_tuple(h, (f | str).value, v ? "True" : "False");
}

/** Logging conversion for floating points. */
template <log_flags... Fs, std::floating_point V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, str);
    prohibit(f, unsigned_val);

    // Add 'floating' flag, force to double for variadic passing.
    return std::make_tuple(h, (f | floating).value, static_cast<double>(v));
}

/** Logging conversion for sdbusplus enums. */
template <log_flags... Fs, sdbusplus_enum V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    return std::make_tuple(h, (f | str).value,
                           sdbusplus::message::convert_to_string(v));
}

/** Logging conversion for string-likes. */
template <log_flags... Fs, string_like_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V&& v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Utiilty to handle conversion to a 'const char*' depending on V:
    //  - 'const char*' and similar use static cast.
    //  - 'std::filesystem::path' use c_str() function.
    //  - 'std::string' and 'std::string_view' use data() function.
    auto str_data = [](V&& v) {
        if constexpr (std::is_same_v<const char*, std::decay_t<V>> ||
                      std::is_same_v<char*, std::decay_t<V>>)
        {
            return static_cast<const char*>(v);
        }
        else if constexpr (std::is_same_v<std::filesystem::path,
                                          std::decay_t<V>>)
        {
            return v.c_str();
        }
        else
        {
            return v.data();
        }
    };

    // Add 'str' flag, force to 'const char*' for variadic passing.
    return std::make_tuple(h, (f | str).value, str_data(std::forward<V>(v)));
}

/** Logging conversion for pointer-types. */
template <log_flags... Fs, pointer_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, signed_val);
    prohibit(f, str);

    // Cast (void*) to a hex-formatted uint64 using the target's pointer-size
    // to determine field-width.
    constexpr static auto new_f =
        sizeof(void*) == 4 ? field32.value : field64.value;

    return std::make_tuple(h, new_f | (hex | unsigned_val).value,
                           reinterpret_cast<uint64_t>(v));
}

/* Logging conversion for exceptions. */
template <log_flags... Fs, exception_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V&& v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Treat like a string, but get the 'what' from the exception.
    return std::make_tuple(h, (f | str).value, v.what());
}

/** Class to facilitate walking through the arguments of the `lg2::log` function
 *  and ensuring correct parameter types and conversion operations.
 */
class log_conversion
{
  private:
    /** Conversion and validation is complete.  Pass along to the final
     *  do_log variadic function. */
    template <typename... Ts>
    static void done(level l, const lg2::source_location& s, const char* m,
                     Ts&&... ts)
    {
        do_log(l, s, m, std::forward<Ts>(ts)..., nullptr);
    }

    /** Apply the tuple from the end of 'step' into done.
     *
     *  There are some cases where the tuple must hold a `std::string` in
     *  order to avoid losing data in a temporary (see sdbusplus-enum
     *  conversion), but the `do_log` function wants a `const char*` as
     *  the variadic type.  Run the tuple through a lambda to pull out
     *  the `const char*`'s without losing the temporary held by the tuple.
     */
    static void apply_done(const auto& args_tuple)
    {
        auto squash_string = [](auto& arg) -> decltype(auto) {
            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
            {
                return arg.data();
            }
            else
            {
                return arg;
            }
        };

        std::apply([squash_string](
                       const auto&... args) { done(squash_string(args)...); },
                   args_tuple);
    }

    /** Handle conversion of a { Header, Flags, Value } argument set. */
    template <typename... Ts, log_flags... Fs, typename V, typename... Ss>
    static void step(std::tuple<Ts...>&& ts, const header_str& h,
                     log_flag<Fs...> f, V&& v, Ss&&... ss)
    {
        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
                      "Found header_str as value; suggest using std::string to "
                      "avoid unintended conversion.");

        // These two if conditions are similar, except that one calls 'done'
        // since Ss is empty and the other calls the next 'step'.

        // 1. Call `log_convert` on {h, f, v} for proper conversion.
        // 2. Append the results of `log_convert` into the already handled
        //    arguments (in ts).
        // 3. Call the next step in the chain to handle the remainder Ss.

        if constexpr (sizeof...(Ss) == 0)
        {
            apply_done(
                std::tuple_cat(std::move(ts), log_convert(h.data(), f, v)));
        }
        else
        {
            step(std::tuple_cat(std::move(ts), log_convert(h.data(), f, v)),
                 std::forward<Ss>(ss)...);
        }
    }

    /** Handle conversion of a { Header, Value } argument set. */
    template <typename... Ts, typename V, typename... Ss>
    static void step(std::tuple<Ts...>&& ts, const header_str& h, V&& v,
                     Ss&&... ss)
    {
        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
                      "Found header_str as value; suggest using std::string to "
                      "avoid unintended conversion.");
        // These two if conditions are similar, except that one calls 'done'
        // since Ss is empty and the other calls the next 'step'.

        // 1. Call `log_convert` on {h, <empty flags>, v} for proper conversion.
        // 2. Append the results of `log_convert` into the already handled
        //    arguments (in ts).
        // 3. Call the next step in the chain to handle the remainder Ss.

        if constexpr (sizeof...(Ss) == 0)
        {
            apply_done(std::tuple_cat(std::move(ts),
                                      log_convert(h.data(), log_flag<>{}, v)));
        }
        else
        {
            step(std::tuple_cat(std::move(ts),
                                log_convert(h.data(), log_flag<>{}, v)),
                 std::forward<Ss>(ss)...);
        }
    }

    /** Finding a non-string as the first argument of a 2 or 3 argument set
     *  is an error (missing HEADER field). */
    template <typename... Ts, not_constexpr_string H, typename... Ss>
    static void step(std::tuple<Ts...>&&, H, Ss&&...)
    {
        static_assert(std::is_same_v<header_str, H>,
                      "Found value without expected header field.");
    }

    /** Finding a free string at the end is an error (found HEADER but no data).
     */
    template <typename... Ts>
    static void step(std::tuple<Ts...>&&, header_str)
    {
        static_assert(!std::is_same_v<header_str, header_str>,
                      "Found header field without expected data.");
    }

  public:
    /** Start processing a sequence of arguments to `lg2::log` using `step` or
     * `done`. */
    template <typename... Ts>
    static void start(level l, const lg2::source_location& s, const char* msg,
                      Ts&&... ts)
    {
        // If there are no arguments (ie. just a message), then skip processing
        // and call `done` directly.
        if constexpr (sizeof...(Ts) == 0)
        {
            done(l, s, msg);
        }
        // Handle all the Ts by recursively calling 'step'.
        else
        {
            step(std::forward_as_tuple(l, s, msg), std::forward<Ts>(ts)...);
        }
    }
};

} // namespace lg2::details
<commit_msg>lg2: fix clang issue on nullptr_t<commit_after>#pragma once

#include <concepts>
#include <cstddef>
#include <filesystem>
#include <phosphor-logging/lg2/flags.hpp>
#include <phosphor-logging/lg2/header.hpp>
#include <phosphor-logging/lg2/level.hpp>
#include <phosphor-logging/lg2/logger.hpp>
#include <phosphor-logging/lg2/source_location.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <string_view>
#include <tuple>
#include <type_traits>

namespace lg2::details
{

/** Concept to determine if an item acts like a string.
 *
 *  Something acts like a string if we can construct a string_view from it.
 *  This covers std::string and C-strings.  But, there is subtlety in that
 *  nullptr_t can be used to construct a string_view until C++23, so we need
 *  to exempt out nullptr_t's (otherwise `nullptr` ends up acting like a
 *  string).
 */
template <typename T>
concept string_like_type =
    (std::constructible_from<std::string_view, T> ||
     std::same_as<std::filesystem::path,
                  std::decay_t<T>>)&&!std::same_as<std::nullptr_t, T>;

/** Concept to determine if an item acts like a pointer.
 *
 *  Any pointer, which doesn't act like a string, should be treated as a raw
 *  pointer.
 */
template <typename T>
concept pointer_type = (std::is_pointer_v<T> ||
                        std::same_as<std::nullptr_t, T>)&&!string_like_type<T>;

/** Concept to determine if an item acts like an unsigned_integral.
 *
 *  Except bool because we want bool to be handled special to create nice
 *  `True` and `False` strings.
 */
template <typename T>
concept unsigned_integral_except_bool =
    !std::same_as<T, bool> && std::unsigned_integral<T>;

template <typename T>
concept sdbusplus_enum = sdbusplus::message::has_convert_from_string_v<T>;

template <typename T>
concept exception_type = std::derived_from<std::decay_t<T>, std::exception>;

/** Concept listing all of the types we know how to convert into a format
 *  for logging.
 */
template <typename T>
concept unsupported_log_convert_types =
    !(unsigned_integral_except_bool<T> || std::signed_integral<T> ||
      std::same_as<bool, T> || std::floating_point<T> || string_like_type<T> ||
      pointer_type<T> || sdbusplus_enum<T> || exception_type<T>);

/** Any type we do not know how to convert for logging gives a nicer
 *  static_assert message. */
template <log_flags... Fs, unsupported_log_convert_types T>
static auto log_convert(const char*, log_flag<Fs...>, T)
{
    static_assert(!std::is_same_v<T, T>, "Unsupported type for logging value.");
    // Having this return of an empty tuple reduces the error messages.
    return std::tuple<>{};
}

/** Logging conversion for unsigned. */
template <log_flags... Fs, unsigned_integral_except_bool V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, floating);
    prohibit(f, signed_val);
    prohibit(f, str);

    one_from_set(f, dec | hex | bin);
    one_from_set(f, field8 | field16 | field32 | field64);

    // Add 'unsigned' flag, force to uint64_t for variadic passing.
    return std::make_tuple(h, (f | unsigned_val).value,
                           static_cast<uint64_t>(v));
}

/** Logging conversion for signed. */
template <log_flags... Fs, std::signed_integral V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, floating);
    prohibit(f, str);
    prohibit(f, unsigned_val);

    one_from_set(f, dec | hex | bin);
    one_from_set(f, field8 | field16 | field32 | field64);

    // Add 'signed' flag, force to int64_t for variadic passing.
    return std::make_tuple(h, (f | signed_val).value, static_cast<int64_t>(v));
}

/** Logging conversion for bool. */
template <log_flags... Fs, std::same_as<bool> V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Cast bools to a "True" or "False" string.
    return std::make_tuple(h, (f | str).value, v ? "True" : "False");
}

/** Logging conversion for floating points. */
template <log_flags... Fs, std::floating_point V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, str);
    prohibit(f, unsigned_val);

    // Add 'floating' flag, force to double for variadic passing.
    return std::make_tuple(h, (f | floating).value, static_cast<double>(v));
}

/** Logging conversion for sdbusplus enums. */
template <log_flags... Fs, sdbusplus_enum V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    return std::make_tuple(h, (f | str).value,
                           sdbusplus::message::convert_to_string(v));
}

/** Logging conversion for string-likes. */
template <log_flags... Fs, string_like_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V&& v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Utiilty to handle conversion to a 'const char*' depending on V:
    //  - 'const char*' and similar use static cast.
    //  - 'std::filesystem::path' use c_str() function.
    //  - 'std::string' and 'std::string_view' use data() function.
    auto str_data = [](V&& v) {
        if constexpr (std::is_same_v<const char*, std::decay_t<V>> ||
                      std::is_same_v<char*, std::decay_t<V>>)
        {
            return static_cast<const char*>(v);
        }
        else if constexpr (std::is_same_v<std::filesystem::path,
                                          std::decay_t<V>>)
        {
            return v.c_str();
        }
        else
        {
            return v.data();
        }
    };

    // Add 'str' flag, force to 'const char*' for variadic passing.
    return std::make_tuple(h, (f | str).value, str_data(std::forward<V>(v)));
}

/** Logging conversion for pointer-types. */
template <log_flags... Fs, pointer_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, signed_val);
    prohibit(f, str);

    // Cast (void*) to a hex-formatted uint64 using the target's pointer-size
    // to determine field-width.
    constexpr static auto new_f =
        sizeof(void*) == 4 ? field32.value : field64.value;

    return std::make_tuple(h, new_f | (hex | unsigned_val).value,
                           reinterpret_cast<uint64_t>(v));
}

/* Logging conversion for exceptions. */
template <log_flags... Fs, exception_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V&& v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Treat like a string, but get the 'what' from the exception.
    return std::make_tuple(h, (f | str).value, v.what());
}

/** Class to facilitate walking through the arguments of the `lg2::log` function
 *  and ensuring correct parameter types and conversion operations.
 */
class log_conversion
{
  private:
    /** Conversion and validation is complete.  Pass along to the final
     *  do_log variadic function. */
    template <typename... Ts>
    static void done(level l, const lg2::source_location& s, const char* m,
                     Ts&&... ts)
    {
        do_log(l, s, m, std::forward<Ts>(ts)..., nullptr);
    }

    /** Apply the tuple from the end of 'step' into done.
     *
     *  There are some cases where the tuple must hold a `std::string` in
     *  order to avoid losing data in a temporary (see sdbusplus-enum
     *  conversion), but the `do_log` function wants a `const char*` as
     *  the variadic type.  Run the tuple through a lambda to pull out
     *  the `const char*`'s without losing the temporary held by the tuple.
     */
    static void apply_done(const auto& args_tuple)
    {
        auto squash_string = [](auto& arg) -> decltype(auto) {
            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
            {
                return arg.data();
            }
            else
            {
                return arg;
            }
        };

        std::apply([squash_string](
                       const auto&... args) { done(squash_string(args)...); },
                   args_tuple);
    }

    /** Handle conversion of a { Header, Flags, Value } argument set. */
    template <typename... Ts, log_flags... Fs, typename V, typename... Ss>
    static void step(std::tuple<Ts...>&& ts, const header_str& h,
                     log_flag<Fs...> f, V&& v, Ss&&... ss)
    {
        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
                      "Found header_str as value; suggest using std::string to "
                      "avoid unintended conversion.");

        // These two if conditions are similar, except that one calls 'done'
        // since Ss is empty and the other calls the next 'step'.

        // 1. Call `log_convert` on {h, f, v} for proper conversion.
        // 2. Append the results of `log_convert` into the already handled
        //    arguments (in ts).
        // 3. Call the next step in the chain to handle the remainder Ss.

        if constexpr (sizeof...(Ss) == 0)
        {
            apply_done(
                std::tuple_cat(std::move(ts), log_convert(h.data(), f, v)));
        }
        else
        {
            step(std::tuple_cat(std::move(ts), log_convert(h.data(), f, v)),
                 std::forward<Ss>(ss)...);
        }
    }

    /** Handle conversion of a { Header, Value } argument set. */
    template <typename... Ts, typename V, typename... Ss>
    static void step(std::tuple<Ts...>&& ts, const header_str& h, V&& v,
                     Ss&&... ss)
    {
        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
                      "Found header_str as value; suggest using std::string to "
                      "avoid unintended conversion.");
        // These two if conditions are similar, except that one calls 'done'
        // since Ss is empty and the other calls the next 'step'.

        // 1. Call `log_convert` on {h, <empty flags>, v} for proper conversion.
        // 2. Append the results of `log_convert` into the already handled
        //    arguments (in ts).
        // 3. Call the next step in the chain to handle the remainder Ss.

        if constexpr (sizeof...(Ss) == 0)
        {
            apply_done(std::tuple_cat(std::move(ts),
                                      log_convert(h.data(), log_flag<>{}, v)));
        }
        else
        {
            step(std::tuple_cat(std::move(ts),
                                log_convert(h.data(), log_flag<>{}, v)),
                 std::forward<Ss>(ss)...);
        }
    }

    /** Finding a non-string as the first argument of a 2 or 3 argument set
     *  is an error (missing HEADER field). */
    template <typename... Ts, not_constexpr_string H, typename... Ss>
    static void step(std::tuple<Ts...>&&, H, Ss&&...)
    {
        static_assert(std::is_same_v<header_str, H>,
                      "Found value without expected header field.");
    }

    /** Finding a free string at the end is an error (found HEADER but no data).
     */
    template <typename... Ts>
    static void step(std::tuple<Ts...>&&, header_str)
    {
        static_assert(!std::is_same_v<header_str, header_str>,
                      "Found header field without expected data.");
    }

  public:
    /** Start processing a sequence of arguments to `lg2::log` using `step` or
     * `done`. */
    template <typename... Ts>
    static void start(level l, const lg2::source_location& s, const char* msg,
                      Ts&&... ts)
    {
        // If there are no arguments (ie. just a message), then skip processing
        // and call `done` directly.
        if constexpr (sizeof...(Ts) == 0)
        {
            done(l, s, msg);
        }
        // Handle all the Ts by recursively calling 'step'.
        else
        {
            step(std::forward_as_tuple(l, s, msg), std::forward<Ts>(ts)...);
        }
    }
};

} // namespace lg2::details
<|endoftext|>


<commit_before>/******************************************************************************
* YawController.cpp
*
* Contains the yaw_controller() function
******************************************************************************/
#include "Mapper.h"

/******************************************************************************
* int yaw_controller()
*
* Takes in readings from IMU and calculates a percentage (-1 to 1) to run
* motors at
******************************************************************************/
/*
int yaw_controller()
{
	// control output //
	if( bno055.yaw < 180 ) // AUV is pointed right
	{
		// u[2] is negative
		motor_percent = yaw_pid.kp*(bno055.yaw - yaw_pid.setpoint)
			+ KD_YAW*(sstate.yaw[0]-sstate.yaw[1])/DT; // yaw controller
	}
	else		// AUV is pointed left
	{
		// u[2] is positive
		motor_percent = yaw_pid.kp*(yaw_pid.setpoint-(bno055.yaw-360))
			+ KD_YAW*(sstate.yaw[0]-sstate.yaw[1])/DT; // yaw controller
	}
	// saturate yaw controller //
	if( u[2] > YAW_SAT )
	{
		motor_percent=YAW_SAT;
	}
	else if( motor_percent < -YAW_SAT )
	{
		motor_percent = -YAW_SAT;
	}

	// set current yaw to be the old yaw //
	yaw_pid.oldyaw = bno055.yaw;

	// set starboard positive and port negative //
	starboard_percent = motor_percent;
	port_percent = -motor_percent;

	// set current yaw to be the old yaw //
	yaw_pid.oldyaw = bno055.yaw;

	return 0;
}
*/
<commit_msg>Changed Controls.cpp to return motor_percent<commit_after>/******************************************************************************
* YawController.cpp
*
* Contains the yaw_controller() function
******************************************************************************/
#include "Mapper.h"

/******************************************************************************
* float yaw_controller()
*
* Takes in readings from IMU and calculates a percentage (-1 to 1) to run
* motors at
******************************************************************************/
/*
float yaw_controller()
{
	// control output //
	if( bno055.yaw < 180 ) // AUV is pointed right
	{
		// u[2] is negative
		motor_percent = yaw_pid.kp*(bno055.yaw - yaw_pid.setpoint)
			+ KD_YAW*(sstate.yaw[0]-sstate.yaw[1])/DT; // yaw controller
	}
	else		// AUV is pointed left
	{
		// u[2] is positive
		motor_percent = yaw_pid.kp*(yaw_pid.setpoint-(bno055.yaw-360))
			+ KD_YAW*(sstate.yaw[0]-sstate.yaw[1])/DT; // yaw controller
	}
	// saturate yaw controller //
	if( u[2] > YAW_SAT )
	{
		motor_percent=YAW_SAT;
	}
	else if( motor_percent < -YAW_SAT )
	{
		motor_percent = -YAW_SAT;
	}

	// set current yaw to be the old yaw //
	yaw_pid.oldyaw = bno055.yaw;

	// set current yaw to be the old yaw //
	yaw_pid.oldyaw = bno055.yaw;

	return motor_percent;
}
*/
<|endoftext|>


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

#include "NodeCreationDialog.h"

CLANG_DIAG_OFF(deprecated)
CLANG_DIAG_OFF(uninitialized)
#include <QVBoxLayout>
#include <QStringList>
#include <QKeyEvent>
#include <QAbstractItemView>
#include <QTimer>
#include <QApplication>
#include <QListView>
#include <QDesktopWidget>
#include <QApplication>
#include <QStringListModel>
CLANG_DIAG_ON(deprecated)
CLANG_DIAG_ON(uninitialized)

#include "Engine/Plugin.h"
#include "Gui/LineEdit.h"
#include "Gui/GuiApplicationManager.h"

struct CompleterLineEditPrivate
{
    QDialog* dialog;
    QListView* listView;
    QStringListModel* model;
    QStringList words,ids;
    bool quickExitEnabled;

    CompleterLineEditPrivate(const QStringList & displayWords,
                             const QStringList & internalIds,
                             bool quickExit,
                             QDialog* parent)
        : dialog(parent)
          , listView(NULL)
          , model(NULL)
          , words(displayWords)
          , ids(internalIds)
          , quickExitEnabled(quickExit)
    {
        assert( displayWords.size() == internalIds.size() );
    }
};

CompleterLineEdit::CompleterLineEdit(const QStringList & displayWords,
                                     const QStringList & internalIds,
                                     bool quickExit,
                                     QDialog* parent)
    : LineEdit(parent)
      , _imp( new CompleterLineEditPrivate(displayWords,internalIds,quickExit,parent) )
{
    _imp->listView = new QListView(this);
    _imp->model = new QStringListModel(this);
    _imp->listView->setWindowFlags(Qt::ToolTip);
    _imp->listView->setModel(_imp->model);
    connect( this, SIGNAL( textChanged(QString) ), this, SLOT( filterText(QString) ) );
    connect( _imp->listView, SIGNAL( clicked(QModelIndex) ), this, SLOT( setTextFromIndex(QModelIndex) ) );
}

CompleterLineEdit::~CompleterLineEdit()
{
}

QListView*
CompleterLineEdit::getView() const
{
    return _imp->listView;
}

void
CompleterLineEdit::filterText(const QString & txt)
{
    QStringList sl;

    if ( txt.isEmpty() ) {
        sl = _imp->words;
    } else {
        for (int i = 0; i < _imp->ids.size(); ++i) {
            if ( _imp->ids[i].contains(txt,Qt::CaseInsensitive) ) {
                sl << _imp->words[i];
            }
        }
    }
    _imp->model->setStringList(sl);
    _imp->listView->setModel(_imp->model);

    int rowCount = _imp->model->rowCount();
    if (rowCount == 0) {
        return;
    }

    QPoint p = mapToGlobal( QPoint( 0,height() ) );
    QDesktopWidget* desktop = QApplication::desktop();
    QRect screen = desktop->screenGeometry();
    double maxHeight = ( screen.height() - p.y() ) * 0.8;
    QFontMetrics fm = _imp->listView->fontMetrics();
    maxHeight = std::min( maxHeight, ( rowCount * fm.height() * 1.2 + fm.height() ) );

    // Position the text edit
    _imp->listView->resize(width(),maxHeight);

    _imp->listView->move(p);
    _imp->listView->show();
}

void
CompleterLineEdit::setTextFromIndex(const QModelIndex & index)
{
    QString text = index.data().toString();

    setText(text);
    emit itemCompletionChosen();
    _imp->listView->hide();
    if (_imp->quickExitEnabled) {
        _imp->dialog->accept();
    }
}

void
CompleterLineEdit::keyPressEvent(QKeyEvent* e)
{
    int key = e->key();
    bool viewVisible = !_imp->listView->isHidden();

    assert( _imp->listView->model() );
    int count = _imp->listView->model()->rowCount();
    QModelIndex currentIndex = _imp->listView->currentIndex();

    if (key == Qt::Key_Escape) {
        if (_imp->quickExitEnabled) {
            _imp->dialog->reject();
        } else {
            if ( _imp->listView->isVisible() ) {
                _imp->listView->hide();
            } else {
                _imp->dialog->reject();
            }
        }
        e->accept();
    } else if (key == Qt::Key_Down) {
        if (viewVisible) {
            int row = currentIndex.row() + 1;

            ///Handle circular selection
            if (row >= count) {
                row = 0;
            }

            QModelIndex index = _imp->listView->model()->index(row, 0);
            _imp->listView->setCurrentIndex(index);
        }
    } else if (key == Qt::Key_Up) {
        if (viewVisible) {
            int row = currentIndex.row() - 1;

            ///Handle circular selection
            if (row < 0) {
                row = count - 1;
            }

            QModelIndex index = _imp->listView->model()->index(row, 0);
            _imp->listView->setCurrentIndex(index);
        }
    } else if ( (key == Qt::Key_Enter) || (key == Qt::Key_Return) ) {
        _imp->listView->hide();
        if (_imp->model->rowCount() == 1) {
            setText( _imp->model->index(0).data().toString() );
            emit itemCompletionChosen();
            if (_imp->quickExitEnabled) {
                _imp->dialog->accept();
            }
            e->accept();
        } else {
            const QItemSelection selection = _imp->listView->selectionModel()->selection();
            QModelIndexList indexes = selection.indexes();
            if (indexes.size() == 1) {
                setText( _imp->model->index( indexes[0].row() ).data().toString() );
                emit itemCompletionChosen();
                if (_imp->quickExitEnabled) {
                    _imp->dialog->accept();
                }
                e->accept();
            } else {
                QLineEdit::keyPressEvent(e);
            }
        }

    } else {
        QLineEdit::keyPressEvent(e);
    }
} // keyPressEvent

void
CompleterLineEdit::showCompleter()
{
    filterText("");
}

struct NodeCreationDialogPrivate
{
    QVBoxLayout* layout;
    CompleterLineEdit* textEdit;
    std::vector<Natron::Plugin*> items;

    NodeCreationDialogPrivate()
        : layout(NULL)
          , textEdit(NULL)
          , items( appPTR->getPluginsList() )
    {
    }
};

NodeCreationDialog::NodeCreationDialog(QWidget* parent)
    : QDialog(parent)
      , _imp( new NodeCreationDialogPrivate() )
{
    setWindowTitle( tr("Node creation tool") );
    setWindowFlags(Qt::Window | Qt::CustomizeWindowHint);
    setObjectName("nodeCreationDialog");
    setAttribute(Qt::WA_DeleteOnClose,false);
    _imp->layout = new QVBoxLayout(this);
    _imp->layout->setContentsMargins(0, 0, 0, 0);

    QStringList ids;
    QStringList names;
    for (unsigned int i = 0; i < _imp->items.size(); ++i) {
        ids.push_back( _imp->items[i]->getPluginID() );
        names.push_back( _imp->items[i]->getPluginLabel() );
    }
    ids.sort();
    names.sort();
    _imp->textEdit = new CompleterLineEdit(ids,names,true,this);

    QPoint global = QCursor::pos();
    QSize sizeH = sizeHint();
    global.rx() -= sizeH.width() / 2;
    global.ry() -= sizeH.height() / 2;
    move( global.x(), global.y() );

    _imp->layout->addWidget(_imp->textEdit);
    _imp->textEdit->setFocus();
    QTimer::singleShot( 25, _imp->textEdit, SLOT( showCompleter() ) );
}

NodeCreationDialog::~NodeCreationDialog()
{
}

QString
NodeCreationDialog::getNodeName() const
{
    return _imp->textEdit->text();
}

void
NodeCreationDialog::keyPressEvent(QKeyEvent* e)
{
    if ( (e->key() == Qt::Key_Return) || (e->key() == Qt::Key_Enter) ) {
        accept();
    } else if (e->key() == Qt::Key_Escape) {
        reject();
    } else {
        QDialog::keyPressEvent(e);
    }
}

void
NodeCreationDialog::changeEvent(QEvent* e)
{
    if (e->type() == QEvent::ActivationChange) {
        if ( !isActiveWindow() ) {
            reject();

            return;
        }
    }
    QDialog::changeEvent(e);
}

<commit_msg>Fix a bug with the node creation dialog where on windows it would not create the node when clicking an entry in the menu<commit_after>//  Natron
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "NodeCreationDialog.h"

CLANG_DIAG_OFF(deprecated)
CLANG_DIAG_OFF(uninitialized)
#include <QVBoxLayout>
#include <QStringList>
#include <QKeyEvent>
#include <QAbstractItemView>
#include <QTimer>
#include <QApplication>
#include <QListView>
#include <QDesktopWidget>
#include <QApplication>
#include <QStringListModel>
CLANG_DIAG_ON(deprecated)
CLANG_DIAG_ON(uninitialized)

#include "Engine/Plugin.h"
#include "Gui/LineEdit.h"
#include "Gui/GuiApplicationManager.h"

struct CompleterLineEditPrivate
{
    QDialog* dialog;
    QListView* listView;
    QStringListModel* model;
    QStringList words,ids;
    bool quickExitEnabled;

    CompleterLineEditPrivate(const QStringList & displayWords,
                             const QStringList & internalIds,
                             bool quickExit,
                             QDialog* parent)
        : dialog(parent)
          , listView(NULL)
          , model(NULL)
          , words(displayWords)
          , ids(internalIds)
          , quickExitEnabled(quickExit)
    {
        assert( displayWords.size() == internalIds.size() );
    }
};

CompleterLineEdit::CompleterLineEdit(const QStringList & displayWords,
                                     const QStringList & internalIds,
                                     bool quickExit,
                                     QDialog* parent)
    : LineEdit(parent)
      , _imp( new CompleterLineEditPrivate(displayWords,internalIds,quickExit,parent) )
{
    _imp->listView = new QListView(this);
    _imp->model = new QStringListModel(this);
    _imp->listView->setWindowFlags(Qt::ToolTip);
    _imp->listView->setModel(_imp->model);
    connect( this, SIGNAL( textChanged(QString) ), this, SLOT( filterText(QString) ) );
    connect( _imp->listView, SIGNAL( clicked(QModelIndex) ), this, SLOT( setTextFromIndex(QModelIndex) ) );
}

CompleterLineEdit::~CompleterLineEdit()
{
}

QListView*
CompleterLineEdit::getView() const
{
    return _imp->listView;
}

void
CompleterLineEdit::filterText(const QString & txt)
{
    QStringList sl;

    if ( txt.isEmpty() ) {
        sl = _imp->words;
    } else {
        for (int i = 0; i < _imp->ids.size(); ++i) {
            if ( _imp->ids[i].contains(txt,Qt::CaseInsensitive) ) {
                sl << _imp->words[i];
            }
        }
    }
    _imp->model->setStringList(sl);
    _imp->listView->setModel(_imp->model);

    int rowCount = _imp->model->rowCount();
    if (rowCount == 0) {
        return;
    }

    QPoint p = mapToGlobal( QPoint( 0,height() ) );
    QDesktopWidget* desktop = QApplication::desktop();
    QRect screen = desktop->screenGeometry();
    double maxHeight = ( screen.height() - p.y() ) * 0.8;
    QFontMetrics fm = _imp->listView->fontMetrics();
    maxHeight = std::min( maxHeight, ( rowCount * fm.height() * 1.2 + fm.height() ) );

    // Position the text edit
    _imp->listView->resize(width(),maxHeight);

    _imp->listView->move(p);
    _imp->listView->show();
}

void
CompleterLineEdit::setTextFromIndex(const QModelIndex & index)
{
    QString text = index.data().toString();

    setText(text);
    emit itemCompletionChosen();
    _imp->listView->hide();
    if (_imp->quickExitEnabled) {
        _imp->dialog->accept();
    }
}

void
CompleterLineEdit::keyPressEvent(QKeyEvent* e)
{
    int key = e->key();
    bool viewVisible = !_imp->listView->isHidden();

    assert( _imp->listView->model() );
    int count = _imp->listView->model()->rowCount();
    QModelIndex currentIndex = _imp->listView->currentIndex();

    if (key == Qt::Key_Escape) {
        if (_imp->quickExitEnabled) {
            _imp->dialog->reject();
        } else {
            if ( _imp->listView->isVisible() ) {
                _imp->listView->hide();
            } else {
                _imp->dialog->reject();
            }
        }
        e->accept();
    } else if (key == Qt::Key_Down) {
        if (viewVisible) {
            int row = currentIndex.row() + 1;

            ///Handle circular selection
            if (row >= count) {
                row = 0;
            }

            QModelIndex index = _imp->listView->model()->index(row, 0);
            _imp->listView->setCurrentIndex(index);
        }
    } else if (key == Qt::Key_Up) {
        if (viewVisible) {
            int row = currentIndex.row() - 1;

            ///Handle circular selection
            if (row < 0) {
                row = count - 1;
            }

            QModelIndex index = _imp->listView->model()->index(row, 0);
            _imp->listView->setCurrentIndex(index);
        }
    } else if ( (key == Qt::Key_Enter) || (key == Qt::Key_Return) ) {
        _imp->listView->hide();
        if (_imp->model->rowCount() == 1) {
            setText( _imp->model->index(0).data().toString() );
            emit itemCompletionChosen();
            if (_imp->quickExitEnabled) {
                _imp->dialog->accept();
            }
            e->accept();
        } else {
            const QItemSelection selection = _imp->listView->selectionModel()->selection();
            QModelIndexList indexes = selection.indexes();
            if (indexes.size() == 1) {
                setText( _imp->model->index( indexes[0].row() ).data().toString() );
                emit itemCompletionChosen();
                if (_imp->quickExitEnabled) {
                    _imp->dialog->accept();
                }
                e->accept();
            } else {
                QLineEdit::keyPressEvent(e);
            }
        }

    } else {
        QLineEdit::keyPressEvent(e);
    }
} // keyPressEvent

void
CompleterLineEdit::showCompleter()
{
    filterText("");
}

struct NodeCreationDialogPrivate
{
    QVBoxLayout* layout;
    CompleterLineEdit* textEdit;
    std::vector<Natron::Plugin*> items;

    NodeCreationDialogPrivate()
        : layout(NULL)
          , textEdit(NULL)
          , items( appPTR->getPluginsList() )
    {
    }
};

NodeCreationDialog::NodeCreationDialog(QWidget* parent)
    : QDialog(parent)
      , _imp( new NodeCreationDialogPrivate() )
{
    setWindowTitle( tr("Node creation tool") );
    setWindowFlags(Qt::Window | Qt::CustomizeWindowHint);
    setObjectName("nodeCreationDialog");
    setAttribute(Qt::WA_DeleteOnClose,false);
    _imp->layout = new QVBoxLayout(this);
    _imp->layout->setContentsMargins(0, 0, 0, 0);

    QStringList ids;
    QStringList names;
    for (unsigned int i = 0; i < _imp->items.size(); ++i) {
        ids.push_back( _imp->items[i]->getPluginID() );
        names.push_back( _imp->items[i]->getPluginLabel() );
    }
    ids.sort();
    names.sort();
    _imp->textEdit = new CompleterLineEdit(ids,names,true,this);

    QPoint global = QCursor::pos();
    QSize sizeH = sizeHint();
    global.rx() -= sizeH.width() / 2;
    global.ry() -= sizeH.height() / 2;
    move( global.x(), global.y() );

    _imp->layout->addWidget(_imp->textEdit);
    _imp->textEdit->setFocus();
    QTimer::singleShot( 25, _imp->textEdit, SLOT( showCompleter() ) );
}

NodeCreationDialog::~NodeCreationDialog()
{
}

QString
NodeCreationDialog::getNodeName() const
{
    return _imp->textEdit->text();
}

void
NodeCreationDialog::keyPressEvent(QKeyEvent* e)
{
    if ( (e->key() == Qt::Key_Return) || (e->key() == Qt::Key_Enter) ) {
        accept();
    } else if (e->key() == Qt::Key_Escape) {
        reject();
    } else {
        QDialog::keyPressEvent(e);
    }
}

void
NodeCreationDialog::changeEvent(QEvent* e)
{
    if (e->type() == QEvent::ActivationChange) {
        if ( !isActiveWindow() && qApp->activeWindow() != _imp->textEdit->getView() ) {
            reject();

            return;
        }
    }
    QDialog::changeEvent(e);
}

<|endoftext|>


<commit_before>#include <jni.h>
#include <stdlib.h>

#include "lib/Log.h"
#include "data_core/base/HashTable.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <fcntl.h>

#include <sys/epoll.h>
#include <pthread.h>

#include <errno.h>
#define MAXBUFLEN 	1024
#define MAXEVENTS 100

void test(signed char * message);
static void make_sendipv4addr(struct sockaddr_in *addr, int remoteport);
int setSend(const char *ipAddr);
int sendPackege(int sockd, const void * buffer, int PackegeSize, unsigned int mode);
static void make_recvipv4addr(struct sockaddr_in *addr, const int localport);
int recvPacket(int sockd);
static unsigned short GetSocketPort(int sd);
int setNonBlocking(int sock);
void setEpoll(int sock);
int sendData(int sockd, const char *buffer);
void sendPackeges(int sockd, const char *buffer);
void test2();
void epollLooper(int epollFD);

int epollFD = 0;
int listeningSocketFD = 0;
int connectingSocketFD = 0;
int sendingSocketFD = 0;

int PackegeSize = 1024;

int dataLength = 0;
int sentLength = 0;
int packegesNum = 0;
int lastPackegeSize = 0;
int sentRuturnLength = 0;
const char *dataBuffer;
bool isSocketBufferFull = false;

char target[15] = "";

//#define  LOG_TAG    "OpenHttp"
//#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
extern "C"
JNIEXPORT jint Java_com_open_clib_MyHttpJNI_nativeSend(JNIEnv *env, jobject obj, jbyteArray ip, jbyteArray url, jint method, jbyteArray header, jbyteArray body, jint start, jint length, jint id) {

	char * ip_buffer;
	char * url_buffer;
	char * header_buffer;

	signed char * body_buffer = (signed char*) malloc(length * sizeof(char));
	env->GetByteArrayRegion(body, start, length, body_buffer);
	Log("hello");

	return (jint) 1;
}

extern "C"
JNIEXPORT jint Java_com_open_clib_MyHttpJNI_test(JNIEnv *env, jobject obj, jbyteArray message) {

	int length = env->GetArrayLength(message);
	signed char * body_buffer = (signed char*) malloc(length + 1 * sizeof(char));
	env->GetByteArrayRegion(message, 0, length, body_buffer);
	body_buffer[length] = 0;
	Log((const char*) body_buffer);

//	test(body_buffer);
	test2();
	return (jint) 1;
}

void test(signed char * message) {
	char * buffer = (char*) malloc(1024 * 3 * sizeof(char));
	for (int i = 1; i < 1024 * 3 - 2; i++) {
		*(buffer + i) = i / 100 + 1;
	}
	*(buffer + 1024 * 3 - 1) = 0;

	int socket = setSend("192.168.1.7");
//	sendPacket(socket,
//			"GET /index.html HTTP/1.1\r\nHost: www.testhttp.com\r\n\r\n\r\n\r\n");
//	sendPacket(socket, buffer);
	char target[15] = "";
	parseNubmerToString((int) GetSocketPort(socket), target);
//	parseNubmerToString(9555444, target);
	Log((const char*) "Connected @ ");
	Log((const char*) target);
	recvPacket(socket);
}

void *epollLooperThread(void *arg) {
	epollLooper(epollFD);
}
void test2() {

	char * buffer = (char*) malloc(1024 * 3 * sizeof(char));
	for (int i = 1; i < 1024 * 3 - 2; i++) {
		*(buffer + i) = i / 100 + 1;
	}
	*(buffer + 1024 * 3 - 1) = 0;

	sendingSocketFD = setSend("192.168.1.7");
	setEpoll(sendingSocketFD);

	pthread_t epollLooperPthread;
	int ret = pthread_create(&epollLooperPthread, NULL, epollLooperThread, (void *) 1);
	sleep(1);
	sendData(sendingSocketFD, buffer);

}

void epollLooper(int epollFD) {

	struct epoll_event events[MAXEVENTS];
	int numEvents = 0;
	Log((const char*) "epollLooper started ! ");
	while (true) {
		numEvents = epoll_wait(epollFD, events, MAXEVENTS, 1000);
		Log((const char*) "epollLooper events");

		for (int i = 0; i < numEvents; ++i) {
			Log((const char*) "resolve event");
			if (events[i].data.fd == listeningSocketFD) {
				Log((const char*) "resolve event  新建连接");
				struct sockaddr clientAddress;
				socklen_t clientaddrLen = (socklen_t) sizeof(clientAddress);
				connectingSocketFD = accept(listeningSocketFD, &clientAddress, &(clientaddrLen));
				epoll_event* clientEvent = new epoll_event();
				clientEvent->data.fd = connectingSocketFD;
				clientEvent->events = EPOLLIN | EPOLLET;
				epoll_ctl(epollFD, EPOLL_CTL_ADD, connectingSocketFD, clientEvent);
			}
			if (events[i].events & EPOLLIN) //接收到数据，读socket
			{
				Log((const char*) "resolve event  接收到数据");
				recvPacket(events[i].data.fd);
			}
			if (events[i].events & EPOLLOUT) {
				Log((const char*) "resolve event EPOLLOUT");

				struct epoll_event event = events[i];
				if (events[i].data.fd == sendingSocketFD) {
					isSocketBufferFull = false;
					Log((const char*) "缓冲区可写");
					sendPackeges(sendingSocketFD, dataBuffer);
					event.events = EPOLLIN | EPOLLET;
//					epoll_ctl(epollFD, EPOLL_CTL_MOD, sendingSocketFD, &event);
				}

			}

			{
				//其他的处理
				Log((const char*) "事件@");
				parseNubmerToString(events[i].events, target);
				Log((const char*) target);
			}
		}
	}
}

int sendData(int sockd, const char *buffer) {
	Log((const char*) "sendData");
	dataBuffer = buffer;
	dataLength = strlen(dataBuffer);
	sentLength = 0;

	packegesNum = dataLength / PackegeSize;
	lastPackegeSize = dataLength % PackegeSize;
	if (lastPackegeSize != 0) {
		packegesNum = dataLength / PackegeSize + 1;
	}

	sendPackeges(sockd, dataBuffer);
//	send(sockd, str, strlen(str), 0);
	return 1;
}

void sendPackeges(int sockd, const char *buffer) {
	if (packegesNum <= 0 || sentLength >= dataLength) {
		return;
	}

	Log((const char*) "sendPackeges");
	for (int i = sentLength / PackegeSize; i < packegesNum - 1; i++) {

		int sentPackegeLength = sendPackege(sockd, buffer, PackegeSize, 0);

		if (isSocketBufferFull) {
			return;
		}
		sentLength += sentPackegeLength;
		buffer = buffer + PackegeSize;
	}

	if (lastPackegeSize != 0) {
		sentLength += sendPackege(sockd, buffer, lastPackegeSize, 0);
	} else {
		sentLength += sendPackege(sockd, buffer, PackegeSize, 0);
	}
}

int sendPackege(int sockd, const void * buffer, int PackegeSize, unsigned int mode) {
	Log((const char*) "send ont Packege");
	int sentPackegeLength = send(sockd, buffer, PackegeSize, 0);
	if (sentPackegeLength == -1) {
		if (errno == EAGAIN) {
			sentPackegeLength = PackegeSize;
			isSocketBufferFull = true;
			Log((const char*) "缓冲区已满");
			sentPackegeLength = 0;
		} else {
			sentPackegeLength = 0;
		}
	}
	return sentPackegeLength;
}

static void make_sendipv4addr(struct sockaddr_in *addr, int remoteport) {
	memset(addr, 0, sizeof(struct sockaddr_in));
	addr->sin_family = AF_INET;
	addr->sin_port = htons(remoteport);
}

int setSend(const char *ipAddr) {
	int sockd = 0;
	struct sockaddr_in addr;
	make_sendipv4addr(&addr, 8091);
	sockd = socket(AF_INET, SOCK_STREAM, 0);
	addr.sin_addr.s_addr = inet_addr("192.168.1.7");

	struct sockaddr_in serveraddr;
	make_recvipv4addr(&serveraddr, 6868);
	int isReUsedPort = 1;
	int sendBuffSize = 1024;
	setsockopt(sockd, SOL_SOCKET, SO_REUSEADDR, &(isReUsedPort), sizeof(isReUsedPort));
	setsockopt(sockd, SOL_SOCKET, SO_SNDBUF, &sendBuffSize, sizeof(sendBuffSize));

	if (-1 == bind(sockd, (struct sockaddr *) &serveraddr, sizeof(serveraddr))) {
		Log((const char*) "bind fail !\r\n");
		return -1;
	}
	Log((const char*) "bind ok !\r\n");

//	if (-1 == listen(sockd, 5)) {
//		Log((const char*) "listen fail !\r\n");
//		return -1;
//	}
//	Log((const char*) "listen ok\r\n");

	int status = connect(sockd, (struct sockaddr *) &addr, sizeof(addr));

	if (status != 0) {

		parseNubmerToString(errno, target);
		Log((const char*) target);
		Log((const char*) "Connect fail!\n");
		return -1;
	}
	Log((const char*) "Connected\n");
	setNonBlocking(sockd);
	if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
//non-blocking模式下无新connection请求，跳出while (1)
	}
	return sockd;
}

void setEpoll(int sock) {
	epollFD = epoll_create(10);
	struct epoll_event event;

	event.data.fd = (sock);
	event.events = EPOLLIN | EPOLLOUT | EPOLLET;

	epoll_ctl(epollFD, EPOLL_CTL_ADD, sock, &event);

	parseNubmerToString(epollFD, target);
	Log((const char*) "setEpoll@");
	Log((const char*) target);

}

int setNonBlocking(int sock) {
	int flags = 0;

	flags = fcntl(sock, F_GETFL, 0);
	flags |= O_NONBLOCK;
	fcntl(sock, F_SETFL, flags);

	return 1;
}

//返回当前socket绑定的端口
static unsigned short GetSocketPort(int sd) {
	unsigned short port = 0;
	struct sockaddr_in address;
	socklen_t addressLength = sizeof(address);
	if (-1 == getsockname(sd, (struct sockaddr*) &address, &addressLength)) {
	} else {
		port = ntohs(address.sin_port);
	}
	return port;
}

//int sendPacket(int sockd, const char *str) {
////	char data[MAXBUFLEN] = { 0 };
////	strcpy(data, str);
//	send(sockd, str, strlen(str), 0);
//	parseNubmerToString(strlen(str), target);
//	Log((const char*) target);
//	return 1;
//}

static void make_recvipv4addr(struct sockaddr_in *addr, const int localport) {
	memset(addr, 0, sizeof(struct sockaddr_in));

	addr->sin_family = AF_INET;
	addr->sin_port = htons(localport);
	addr->sin_addr.s_addr = INADDR_ANY;
}

int setupRecv() {
	int optval = 1;
	int socklsn = 0;
	struct sockaddr_in serveraddr;

	make_recvipv4addr(&serveraddr, 6868);

	socklsn = socket(AF_INET, SOCK_STREAM, 0);

	setsockopt(optval, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

	bind(socklsn, (struct sockaddr *) &serveraddr, sizeof(serveraddr));
	return socklsn;
}

int recvPacket(int sockd) {
	char packet[MAXBUFLEN] = { 0 };
	char * packetPtr = packet;

	int nBytesNeed = MAXBUFLEN;
	int nBytesRecv = 10;

	while (nBytesRecv > 0) {
		Log((const char*) "ready to recv");
		nBytesRecv = recv(sockd, packetPtr, MAXBUFLEN, 0);
		Log((const char*) "received");
		Log((const char*) packetPtr);

	}

	return 1;
}
<commit_msg>Packet data duplication.<commit_after>#include <jni.h>
#include <stdlib.h>

#include "lib/Log.h"
#include "data_core/base/HashTable.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <fcntl.h>

#include <sys/epoll.h>
#include <pthread.h>

#include <errno.h>
#define MAXBUFLEN 	1024
#define MAXEVENTS 100

void test(signed char * message);
static void make_sendipv4addr(struct sockaddr_in *addr, int remoteport);
int setSend(const char *ipAddr);
int sendPackege(int sockd, const void * buffer, int PackegeSize, unsigned int mode);
static void make_recvipv4addr(struct sockaddr_in *addr, const int localport);
int recvPacket(int sockd);
static unsigned short GetSocketPort(int sd);
int setNonBlocking(int sock);
void setEpoll(int sock);
int sendData(int sockd, const char *buffer);
void sendPackeges(int sockd, const char *buffer);
void test2();
void epollLooper(int epollFD);

int epollFD = 0;
int listeningSocketFD = 0;
int connectingSocketFD = 0;
int sendingSocketFD = 0;

int PackegeSize = 1024;

int dataLength = 0;
int sentLength = 0;
int packegesNum = 0;
int lastPackegeSize = 0;
int sentRuturnLength = 0;
const char *dataBuffer;
bool isSocketBufferFull = false;

char target[15] = "";

//#define  LOG_TAG    "OpenHttp"
//#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
extern "C"
JNIEXPORT jint Java_com_open_clib_MyHttpJNI_nativeSend(JNIEnv *env, jobject obj, jbyteArray ip, jbyteArray url, jint method, jbyteArray header, jbyteArray body, jint start, jint length, jint id) {

	char * ip_buffer;
	char * url_buffer;
	char * header_buffer;

	signed char * body_buffer = (signed char*) malloc(length * sizeof(char));
	env->GetByteArrayRegion(body, start, length, body_buffer);
	Log("hello");

	return (jint) 1;
}

extern "C"
JNIEXPORT jint Java_com_open_clib_MyHttpJNI_test(JNIEnv *env, jobject obj, jbyteArray message) {

	int length = env->GetArrayLength(message);
	signed char * body_buffer = (signed char*) malloc(length + 1 * sizeof(char));
	env->GetByteArrayRegion(message, 0, length, body_buffer);
	body_buffer[length] = 0;
	Log((const char*) body_buffer);

//	test(body_buffer);
	test2();
	return (jint) 1;
}

void test(signed char * message) {
	char * buffer = (char*) malloc(1024 * 3 * sizeof(char));
	for (int i = 1; i < 1024 * 3 - 2; i++) {
		*(buffer + i) = i / 100 + 1;
	}
	*(buffer + 1024 * 3 - 1) = 0;

	int socket = setSend("192.168.1.7");
//	sendPacket(socket,
//			"GET /index.html HTTP/1.1\r\nHost: www.testhttp.com\r\n\r\n\r\n\r\n");
//	sendPacket(socket, buffer);
	char target[15] = "";
	parseNubmerToString((int) GetSocketPort(socket), target);
//	parseNubmerToString(9555444, target);
	Log((const char*) "Connected @ ");
	Log((const char*) target);
	recvPacket(socket);
}

void *epollLooperThread(void *arg) {
	epollLooper(epollFD);
}
void test2() {

	char * buffer = (char*) malloc(1024 * 3 * sizeof(char));
	for (int i = 1; i < 1024 * 3 - 2; i++) {
		*(buffer + i) = i / 100 + 1;
	}
	*(buffer + 1024 * 3 - 1) = 0;

	sendingSocketFD = setSend("192.168.1.7");
	setEpoll(sendingSocketFD);

	pthread_t epollLooperPthread;
	int ret = pthread_create(&epollLooperPthread, NULL, epollLooperThread, (void *) 1);
	sleep(1);
	sendData(sendingSocketFD, buffer);

}

void epollLooper(int epollFD) {

	struct epoll_event events[MAXEVENTS];
	int numEvents = 0;
	Log((const char*) "epollLooper started ! ");
	while (true) {
		numEvents = epoll_wait(epollFD, events, MAXEVENTS, 1000);
		Log((const char*) "epollLooper events");

		for (int i = 0; i < numEvents; ++i) {
			Log((const char*) "resolve event");
			if (events[i].data.fd == listeningSocketFD) {
				Log((const char*) "resolve event  新建连接");
				struct sockaddr clientAddress;
				socklen_t clientaddrLen = (socklen_t) sizeof(clientAddress);
				connectingSocketFD = accept(listeningSocketFD, &clientAddress, &(clientaddrLen));
				epoll_event* clientEvent = new epoll_event();
				clientEvent->data.fd = connectingSocketFD;
				clientEvent->events = EPOLLIN | EPOLLET;
				epoll_ctl(epollFD, EPOLL_CTL_ADD, connectingSocketFD, clientEvent);
			}
			if (events[i].events & EPOLLIN) //接收到数据，读socket
			{
				Log((const char*) "resolve event  接收到数据");
				recvPacket(events[i].data.fd);
			}
			if (events[i].events & EPOLLOUT) {
				Log((const char*) "resolve event EPOLLOUT");

				struct epoll_event event = events[i];
				if (events[i].data.fd == sendingSocketFD) {
					isSocketBufferFull = false;
					Log((const char*) "缓冲区可写");
					sendPackeges(sendingSocketFD, dataBuffer);
					event.events = EPOLLIN | EPOLLET;
//					epoll_ctl(epollFD, EPOLL_CTL_MOD, sendingSocketFD, &event);
				}

			}

			{
				//其他的处理
				Log((const char*) "事件@");
				parseNubmerToString(events[i].events, target);
				Log((const char*) target);
			}
		}
	}
}

int sendData(int sockd, const char *buffer) {
	Log((const char*) "sendData");
	dataBuffer = buffer;
	dataLength = strlen(dataBuffer);
	sentLength = 0;

	packegesNum = dataLength / PackegeSize;
	lastPackegeSize = dataLength % PackegeSize;
	if (lastPackegeSize != 0) {
		packegesNum = dataLength / PackegeSize + 1;
	}

	sendPackeges(sockd, dataBuffer);
//	send(sockd, str, strlen(str), 0);
	return 1;
}

void sendPackeges(int sockd, const char *buffer) {
	if (packegesNum <= 0 || sentLength >= dataLength) {
		return;
	}
	buffer = buffer + sentLength;
	Log((const char*) "sendPackeges");
	for (int i = sentLength / PackegeSize; i < packegesNum - 1; i++) {

		int sentPackegeLength = sendPackege(sockd, buffer, PackegeSize, 0);

		if (isSocketBufferFull) {
			return;
		}
		sentLength += sentPackegeLength;
		buffer = buffer + PackegeSize;
	}

	if (lastPackegeSize != 0) {
		sentLength += sendPackege(sockd, buffer, lastPackegeSize, 0);
	} else {
		sentLength += sendPackege(sockd, buffer, PackegeSize, 0);
	}
}

int sendPackege(int sockd, const void * buffer, int PackegeSize, unsigned int mode) {
	Log((const char*) "send ont Packege");
	int sentPackegeLength = send(sockd, buffer, PackegeSize, 0);
	if (sentPackegeLength == -1) {
		if (errno == EAGAIN) {
			sentPackegeLength = PackegeSize;
			isSocketBufferFull = true;
			Log((const char*) "缓冲区已满");
			sentPackegeLength = 0;
		} else {
			sentPackegeLength = 0;
		}
	}
	return sentPackegeLength;
}

static void make_sendipv4addr(struct sockaddr_in *addr, int remoteport) {
	memset(addr, 0, sizeof(struct sockaddr_in));
	addr->sin_family = AF_INET;
	addr->sin_port = htons(remoteport);
}

int setSend(const char *ipAddr) {
	int sockd = 0;
	struct sockaddr_in addr;
	make_sendipv4addr(&addr, 8091);
	sockd = socket(AF_INET, SOCK_STREAM, 0);
	addr.sin_addr.s_addr = inet_addr("192.168.1.7");

	struct sockaddr_in serveraddr;
	make_recvipv4addr(&serveraddr, 6868);
	int isReUsedPort = 1;
	int sendBuffSize = 1024;
	setsockopt(sockd, SOL_SOCKET, SO_REUSEADDR, &(isReUsedPort), sizeof(isReUsedPort));
	setsockopt(sockd, SOL_SOCKET, SO_SNDBUF, &sendBuffSize, sizeof(sendBuffSize));

	if (-1 == bind(sockd, (struct sockaddr *) &serveraddr, sizeof(serveraddr))) {
		Log((const char*) "bind fail !\r\n");
		return -1;
	}
	Log((const char*) "bind ok !\r\n");

//	if (-1 == listen(sockd, 5)) {
//		Log((const char*) "listen fail !\r\n");
//		return -1;
//	}
//	Log((const char*) "listen ok\r\n");

	int status = connect(sockd, (struct sockaddr *) &addr, sizeof(addr));

	if (status != 0) {

		parseNubmerToString(errno, target);
		Log((const char*) target);
		Log((const char*) "Connect fail!\n");
		return -1;
	}
	Log((const char*) "Connected\n");
	setNonBlocking(sockd);
	if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
//non-blocking模式下无新connection请求，跳出while (1)
	}
	return sockd;
}

void setEpoll(int sock) {
	epollFD = epoll_create(10);
	struct epoll_event event;

	event.data.fd = (sock);
	event.events = EPOLLIN | EPOLLOUT | EPOLLET;

	epoll_ctl(epollFD, EPOLL_CTL_ADD, sock, &event);

	parseNubmerToString(epollFD, target);
	Log((const char*) "setEpoll@");
	Log((const char*) target);

}

int setNonBlocking(int sock) {
	int flags = 0;

	flags = fcntl(sock, F_GETFL, 0);
	flags |= O_NONBLOCK;
	fcntl(sock, F_SETFL, flags);

	return 1;
}

//返回当前socket绑定的端口
static unsigned short GetSocketPort(int sd) {
	unsigned short port = 0;
	struct sockaddr_in address;
	socklen_t addressLength = sizeof(address);
	if (-1 == getsockname(sd, (struct sockaddr*) &address, &addressLength)) {
	} else {
		port = ntohs(address.sin_port);
	}
	return port;
}

//int sendPacket(int sockd, const char *str) {
////	char data[MAXBUFLEN] = { 0 };
////	strcpy(data, str);
//	send(sockd, str, strlen(str), 0);
//	parseNubmerToString(strlen(str), target);
//	Log((const char*) target);
//	return 1;
//}

static void make_recvipv4addr(struct sockaddr_in *addr, const int localport) {
	memset(addr, 0, sizeof(struct sockaddr_in));

	addr->sin_family = AF_INET;
	addr->sin_port = htons(localport);
	addr->sin_addr.s_addr = INADDR_ANY;
}

int setupRecv() {
	int optval = 1;
	int socklsn = 0;
	struct sockaddr_in serveraddr;

	make_recvipv4addr(&serveraddr, 6868);

	socklsn = socket(AF_INET, SOCK_STREAM, 0);

	setsockopt(optval, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

	bind(socklsn, (struct sockaddr *) &serveraddr, sizeof(serveraddr));
	return socklsn;
}

int recvPacket(int sockd) {
	char packet[MAXBUFLEN] = { 0 };
	char * packetPtr = packet;

	int nBytesNeed = MAXBUFLEN;
	int nBytesRecv = 10;

	while (nBytesRecv > 0) {
		Log((const char*) "ready to recv");
		nBytesRecv = recv(sockd, packetPtr, MAXBUFLEN, 0);
		Log((const char*) "received");
		Log((const char*) packetPtr);
	}

	return 1;
}
<|endoftext|>


<commit_before>#include "FileStream.h"

#include <Windows.h>
#include <stdio.h>
#include <assert.h>

class FileStreamImpl {

private:
  // Track the number of references to this filestream so
  // that we know whether or not we need to close it when
  // the object gets destroyed.
  uint32 m_ReferenceCount;

  HANDLE m_Handle;

public:
  FileStreamImpl(const CHAR *filename, EFileMode mode)
    : m_ReferenceCount(1)
  {

	DWORD dwDesiredAccess = GENERIC_READ;
	DWORD dwShareMode = 0;
    switch(mode) {
    default:
    case eFileMode_ReadBinary:
    case eFileMode_Read:
      // Do nothing.
      break;

    case eFileMode_Write:
    case eFileMode_WriteBinary:
      dwDesiredAccess = GENERIC_WRITE;
      break;

    case eFileMode_WriteAppend:
    case eFileMode_WriteBinaryAppend:
		dwDesiredAccess = FILE_APPEND_DATA;
      break;
    }

    m_Handle = CreateFile(filename, dwDesiredAccess, dwShareMode, NULL, 0, FILE_ATTRIBUTE_NORMAL, NULL);
	assert(m_Handle != INVALID_HANDLE_VALUE);
  }

  ~FileStreamImpl() {
    CloseHandle(m_Handle);
  }

  void IncreaseReferenceCount() { m_ReferenceCount++; }
  void DecreaseReferenceCount() { m_ReferenceCount--; }

  uint32 GetReferenceCount() { return m_ReferenceCount; }

  HANDLE GetFileHandle() const { return m_Handle; }
};

FileStream::FileStream(const CHAR *filename, EFileMode mode)
  : m_Impl(new FileStreamImpl(filename, mode))
  , m_Mode(mode)
{
  strncpy_s(m_Filename, filename, kMaxFilenameSz);
  m_Filename[kMaxFilenameSz - 1] = CHAR('\0');
}

FileStream::FileStream(const FileStream &other)
  : m_Impl(other.m_Impl)
  , m_Mode(other.m_Mode)
{
  m_Impl->IncreaseReferenceCount();
  strncpy_s(m_Filename, other.m_Filename, kMaxFilenameSz);
}

FileStream &FileStream::operator=(const FileStream &other) {

  // We no longer reference this implementation.
  m_Impl->DecreaseReferenceCount();

  // If we're the last ones to reference it, then it should be destroyed.
  if(m_Impl->GetReferenceCount() <= 0) {
    assert(m_Impl->GetReferenceCount() == 0);
    delete m_Impl;
    m_Impl = 0;
  }

  m_Impl = other.m_Impl;
  m_Impl->IncreaseReferenceCount();

  m_Mode = other.m_Mode;
  strncpy_s(m_Filename, other.m_Filename, kMaxFilenameSz);

  return *this;
}

FileStream::~FileStream() {
  // We no longer reference this implementation.
  m_Impl->DecreaseReferenceCount();

  // If we're the last ones to reference it, then it should be destroyed.
  if(m_Impl->GetReferenceCount() <= 0) {
    assert(m_Impl->GetReferenceCount() == 0);
    delete m_Impl;
    m_Impl = 0;
  }
}

int32 FileStream::Read(uint8 *buf, uint32 bufSz) {

  if(
     m_Mode == eFileMode_Write ||
     m_Mode == eFileMode_WriteBinary ||
     m_Mode == eFileMode_WriteAppend ||
     m_Mode == eFileMode_WriteBinaryAppend
  ) {
    CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Cannot read from file '%s': File opened for reading.", m_Filename);
	OutputDebugString(errStr);
    return -2;
  }

  HANDLE fp = m_Impl->GetFileHandle();
  if(INVALID_HANDLE_VALUE == fp)
    return -1;

  DWORD oldPosition = SetFilePointer(fp, 0, NULL, FILE_CURRENT);
  if(INVALID_SET_FILE_POINTER == oldPosition) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error querying the file position before reading from file '%s'(0x%x).", m_Filename, GetLastError());
	OutputDebugString(errStr);
	return -1;
  }

  DWORD amtRead;
  BOOL success = ReadFile(fp, buf, bufSz, &amtRead, NULL);
  if(!success) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error reading from file '%s'.", m_Filename);
	OutputDebugString(errStr);
    return -1;
  }

  DWORD newPosition = SetFilePointer(fp, 0, NULL, FILE_CURRENT);
  if(INVALID_SET_FILE_POINTER == newPosition) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error querying the file position after reading from file '%s'(0x%x).", m_Filename, GetLastError());
	OutputDebugString(errStr);
	return -1;
  }

  return newPosition - oldPosition;
}

int32 FileStream::Write(const uint8 *buf, uint32 bufSz) {
  if(
     m_Mode == eFileMode_Read ||
     m_Mode == eFileMode_ReadBinary
  ) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Cannot write to file '%s': File opened for writing.", m_Filename);
	OutputDebugString(errStr);
    return -2;
  }

  HANDLE fp = m_Impl->GetFileHandle();
  if(NULL == fp)
    return -1;

  DWORD dwPos;
  if(m_Mode == eFileMode_WriteBinaryAppend || m_Mode == eFileMode_WriteAppend) {
    dwPos = SetFilePointer(fp, 0, NULL, FILE_END);
  }
  else {
    dwPos = SetFilePointer(fp, 0, NULL, FILE_CURRENT);
  }

  if(INVALID_SET_FILE_POINTER == dwPos) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error querying the file position before reading to file '%s'(0x%x).", m_Filename, GetLastError());
	OutputDebugString(errStr);
	return -1;
  }

  while(!LockFile(fp, dwPos, 0, bufSz, 0)) Sleep(1);

  DWORD amtWritten;
  BOOL success = WriteFile(fp, buf, bufSz, &amtWritten, NULL);

  UnlockFile(fp, dwPos, 0, bufSz, 0);

  if(!success) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error writing to file '%s'.", m_Filename);
	OutputDebugString(errStr);
    return -1;
  }

  return amtWritten;
}

int32 FileStream::Tell() {
  HANDLE fp = m_Impl->GetFileHandle();
  if(NULL == fp) {
    return -1;
  }

  DWORD pos =  SetFilePointer(fp, 0, NULL, FILE_CURRENT);
  if(INVALID_SET_FILE_POINTER == pos) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error querying the file position before reading to file '%s'(0x%x).", m_Filename, GetLastError());
	OutputDebugString(errStr);
	return -1;
  }

  return pos;
}

bool FileStream::Seek(uint32 offset, ESeekPosition pos) {

  // We cannot seek in append mode.
  if(m_Mode == eFileMode_WriteAppend || m_Mode == eFileMode_WriteBinaryAppend)
    return false;

  HANDLE fp = m_Impl->GetFileHandle();
  if(NULL == fp) return false;

  DWORD origin = FILE_BEGIN;
  switch(pos) {
  default:
  case eSeekPosition_Beginning:
    // Do nothing
    break;

  case eSeekPosition_Current:
    origin = FILE_CURRENT;
    break;

  case eSeekPosition_End:
    origin = FILE_END;
    break;
  }

  if(SetFilePointer(fp, offset, NULL, origin) == INVALID_SET_FILE_POINTER)
    return false;

  return true;
}

void FileStream::Flush() {
  HANDLE fp = m_Impl->GetFileHandle();
  if(NULL == fp) return;

  FlushFileBuffers(fp);
}
<commit_msg>Fixed a lot of untested bugs with our windows filestream<commit_after>#include "FileStream.h"

#include <Windows.h>
#include <strsafe.h>

#include <stdio.h>
#include <assert.h>

void ErrorExit(LPTSTR lpszFunction)
{
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message and exit the process

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
    StringCchPrintf((LPTSTR)lpDisplayBuf,
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"),
        lpszFunction, dw, lpMsgBuf);
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw);
}

class FileStreamImpl {

private:
  // Track the number of references to this filestream so
  // that we know whether or not we need to close it when
  // the object gets destroyed.
  uint32 m_ReferenceCount;

  HANDLE m_Handle;

public:
  FileStreamImpl(const CHAR *filename, EFileMode mode)
    : m_ReferenceCount(1)
  {

	DWORD dwDesiredAccess = GENERIC_READ;
  DWORD dwOpenAction = OPEN_EXISTING;
  switch(mode) {
    default:
    case eFileMode_ReadBinary:
    case eFileMode_Read:
      // Do nothing.
      break;

    case eFileMode_Write:
    case eFileMode_WriteBinary:
      dwDesiredAccess = GENERIC_WRITE;
      dwOpenAction = CREATE_NEW;
      break;

    case eFileMode_WriteAppend:
    case eFileMode_WriteBinaryAppend:
		  dwDesiredAccess = FILE_APPEND_DATA;
      dwOpenAction = CREATE_NEW;
      break;
    }

    m_Handle = CreateFile(filename, dwDesiredAccess, 0, NULL, dwOpenAction, FILE_ATTRIBUTE_NORMAL, NULL);
	  if(m_Handle == INVALID_HANDLE_VALUE) {
      ErrorExit(TEXT("CreateFile"));
    }
  }

  ~FileStreamImpl() {
    CloseHandle(m_Handle);
  }

  void IncreaseReferenceCount() { m_ReferenceCount++; }
  void DecreaseReferenceCount() { m_ReferenceCount--; }

  uint32 GetReferenceCount() { return m_ReferenceCount; }

  HANDLE GetFileHandle() const { return m_Handle; }
};

FileStream::FileStream(const CHAR *filename, EFileMode mode)
  : m_Impl(new FileStreamImpl(filename, mode))
  , m_Mode(mode)
{
  strncpy_s(m_Filename, filename, kMaxFilenameSz);
  m_Filename[kMaxFilenameSz - 1] = CHAR('\0');
}

FileStream::FileStream(const FileStream &other)
  : m_Impl(other.m_Impl)
  , m_Mode(other.m_Mode)
{
  m_Impl->IncreaseReferenceCount();
  strncpy_s(m_Filename, other.m_Filename, kMaxFilenameSz);
}

FileStream &FileStream::operator=(const FileStream &other) {

  // We no longer reference this implementation.
  m_Impl->DecreaseReferenceCount();

  // If we're the last ones to reference it, then it should be destroyed.
  if(m_Impl->GetReferenceCount() <= 0) {
    assert(m_Impl->GetReferenceCount() == 0);
    delete m_Impl;
    m_Impl = 0;
  }

  m_Impl = other.m_Impl;
  m_Impl->IncreaseReferenceCount();

  m_Mode = other.m_Mode;
  strncpy_s(m_Filename, other.m_Filename, kMaxFilenameSz);

  return *this;
}

FileStream::~FileStream() {
  // We no longer reference this implementation.
  m_Impl->DecreaseReferenceCount();

  // If we're the last ones to reference it, then it should be destroyed.
  if(m_Impl->GetReferenceCount() <= 0) {
    assert(m_Impl->GetReferenceCount() == 0);
    delete m_Impl;
    m_Impl = 0;
  }
}

int32 FileStream::Read(uint8 *buf, uint32 bufSz) {

  if(
     m_Mode == eFileMode_Write ||
     m_Mode == eFileMode_WriteBinary ||
     m_Mode == eFileMode_WriteAppend ||
     m_Mode == eFileMode_WriteBinaryAppend
  ) {
    CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Cannot read from file '%s': File opened for reading.", m_Filename);
	OutputDebugString(errStr);
    return -2;
  }

  HANDLE fp = m_Impl->GetFileHandle();
  if(INVALID_HANDLE_VALUE == fp)
    return -1;

  DWORD oldPosition = SetFilePointer(fp, 0, NULL, FILE_CURRENT);
  if(INVALID_SET_FILE_POINTER == oldPosition) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error querying the file position before reading from file '%s'(0x%x).", m_Filename, GetLastError());
	OutputDebugString(errStr);
	return -1;
  }

  DWORD amtRead;
  BOOL success = ReadFile(fp, buf, bufSz, &amtRead, NULL);
  if(!success) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error reading from file '%s'.", m_Filename);
	OutputDebugString(errStr);
    return -1;
  }

  DWORD newPosition = SetFilePointer(fp, 0, NULL, FILE_CURRENT);
  if(INVALID_SET_FILE_POINTER == newPosition) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error querying the file position after reading from file '%s'(0x%x).", m_Filename, GetLastError());
	OutputDebugString(errStr);
	return -1;
  }

  return newPosition - oldPosition;
}

int32 FileStream::Write(const uint8 *buf, uint32 bufSz) {
  if(
     m_Mode == eFileMode_Read ||
     m_Mode == eFileMode_ReadBinary
  ) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Cannot write to file '%s': File opened for writing.", m_Filename);
	OutputDebugString(errStr);
    return -2;
  }

  HANDLE fp = m_Impl->GetFileHandle();
  if(NULL == fp)
    return -1;

  DWORD dwPos;
  if(m_Mode == eFileMode_WriteBinaryAppend || m_Mode == eFileMode_WriteAppend) {
    dwPos = SetFilePointer(fp, 0, NULL, FILE_END);
  }
  else {
    dwPos = SetFilePointer(fp, 0, NULL, FILE_CURRENT);
  }

  if(INVALID_SET_FILE_POINTER == dwPos) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error querying the file position before reading to file '%s'(0x%x).", m_Filename, GetLastError());
	OutputDebugString(errStr);
	return -1;
  }

  while(!LockFile(fp, dwPos, 0, bufSz, 0)) Sleep(1);

  DWORD amtWritten;
  BOOL success = WriteFile(fp, buf, bufSz, &amtWritten, NULL);

  UnlockFile(fp, dwPos, 0, bufSz, 0);

  if(!success) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error writing to file '%s'.", m_Filename);
	OutputDebugString(errStr);
    return -1;
  }

  return amtWritten;
}

int32 FileStream::Tell() {
  HANDLE fp = m_Impl->GetFileHandle();
  if(NULL == fp) {
    return -1;
  }

  DWORD pos =  SetFilePointer(fp, 0, NULL, FILE_CURRENT);
  if(INVALID_SET_FILE_POINTER == pos) {
	CHAR errStr[256];
	_sntprintf_s(errStr, 256, "Error querying the file position before reading to file '%s'(0x%x).", m_Filename, GetLastError());
	OutputDebugString(errStr);
	return -1;
  }

  return pos;
}

bool FileStream::Seek(uint32 offset, ESeekPosition pos) {

  // We cannot seek in append mode.
  if(m_Mode == eFileMode_WriteAppend || m_Mode == eFileMode_WriteBinaryAppend)
    return false;

  HANDLE fp = m_Impl->GetFileHandle();
  if(NULL == fp) return false;

  DWORD origin = FILE_BEGIN;
  switch(pos) {
  default:
  case eSeekPosition_Beginning:
    // Do nothing
    break;

  case eSeekPosition_Current:
    origin = FILE_CURRENT;
    break;

  case eSeekPosition_End:
    origin = FILE_END;
    break;
  }

  if(SetFilePointer(fp, offset, NULL, origin) == INVALID_SET_FILE_POINTER)
    return false;

  return true;
}

void FileStream::Flush() {
  HANDLE fp = m_Impl->GetFileHandle();
  if(NULL == fp) return;

  FlushFileBuffers(fp);
}
<|endoftext|>