<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/pm/p9_pm_callout.H $       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2017,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file    p9_pm_callout.H
/// @brief   generates a vector of dead cores in response to PM malfunction alert
/// @details In case of PM Malfunction Alert, when HBRT invokes PRD by setting a
//           bit in OCC LFIR, PRD calls this HWP. Procedure walks the bits of
//           OCC Flag2 register and generates a bit vector of cores considered
//           dead by PHYP. It also points to caller a location of FFDC which
//           needs to be committed to an error log. Procedure updates QCSR and
//           CCSR and also cleans up OCC Flag2 and some interrupt registers.
///
///
//----------------------------------------------------------------------------
// *HWP HWP Owner       : Greg Still <stillgs@us.ibm.com>
// *HWP FW Owner        : Prem S Jha <premjha2@in.ibm.com>
// *HWP Team            : PM
// *HWP Level           : 2
// *HWP Consumed by     : HB
//----------------------------------------------------------------------------

#ifndef __P9_PM_CALLOUT
#define __P9_PM_CALLOUT

//------------------------------------------------------------------------------
// Includes
// ------------------------------------------------------------------------------
#include <fapi2.H>

///@brief models a sub-section of STOP Recovery summary.
///@note  caller of HWP passes in an empty vector however,
///HWP populates it with pointers to all summary sub-section.
struct StopErrLogSectn
{
    uint8_t*    iv_pBufPtr;
    uint16_t    iv_bufSize;
    StopErrLogSectn( uint8_t* i_bufPtr, uint16_t i_bufSize )
        : iv_pBufPtr( i_bufPtr ), iv_bufSize( i_bufSize )
    { }

    StopErrLogSectn( )
        : iv_pBufPtr( NULL ),
          iv_bufSize( 0 )
    { }
};

enum RasAction
{
    NO_CALLOUT           =   0,
    FW_CALLOUT           =   1,
    CORE_CALLOUT         =   2,
    PROC_CHIP_CALLOUT    =   3,
    MEMORY_CALLOUT       =   4,
};

// function pointer typedef definition for HWP call support
typedef fapi2::ReturnCode (*p9_pm_callout_FP_t) (
    void* i_pHomer,
    const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_procChipTgt,
    fapi2::buffer <uint32_t>& o_deadCores,
    std::vector < StopErrLogSectn >& o_ffdcList,
    RasAction&   o_rasAction );

extern "C"
{
    /// @brief      analyzes Phyp generated dead core vector for clean up and guard.
    /// @param[in]  i_pHomerBase        points to base of HOMER buffer.
    /// @param[in]  i_procChipTgt       fapi2 target for P9 chip.
    /// @param[out] o_deadCores         bit vector of dead cores which needs to be runtime deconfigured
    /// @param[out] o_ffdcList          points to a list of STOP FFDC summary sub-sections.
    /// @param[out] o_rasAction         RAS action specified for FW.
    /// @return     fapi2 return code.
    /// @note       caller is expected to pass an empty vector. HWP will
    ///             populate a vector with a list pointers to various parts of
    ///             STOP Recovery FFDC summary. Highest priority sub-section
    ///             is pushed to vector first followed by sub-section of lower
    ///             priority. Bit vector is left aligned. Bits to be considered
    ///             are bit0 to bit 23. If bit 0 is set, it means Core0 is
    ///             marked dead by Phyp.
    ///

    fapi2::ReturnCode p9_pm_callout(
        void* i_pHomerBase,
        const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_procChipTgt,
        fapi2::buffer <uint32_t>& o_deadCores,
        std::vector < StopErrLogSectn >& o_ffdcList,
        RasAction&   o_rasAction  );
}
#endif //__P9_PM_CALLOUT
<commit_msg>STOP_Recovery: Added hwp changes to support error log parser.<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/pm/p9_pm_callout.H $       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2017,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file    p9_pm_callout.H
/// @brief   generates a vector of dead cores in response to PM malfunction alert
/// @details In case of PM Malfunction Alert, when HBRT invokes PRD by setting a
//           bit in OCC LFIR, PRD calls this HWP. Procedure walks the bits of
//           OCC Flag2 register and generates a bit vector of cores considered
//           dead by PHYP. It also points to caller a location of FFDC which
//           needs to be committed to an error log. Procedure updates QCSR and
//           CCSR and also cleans up OCC Flag2 and some interrupt registers.
///
///
//----------------------------------------------------------------------------
// *HWP HWP Owner       : Greg Still <stillgs@us.ibm.com>
// *HWP FW Owner        : Prem S Jha <premjha2@in.ibm.com>
// *HWP Team            : PM
// *HWP Level           : 2
// *HWP Consumed by     : HB
//----------------------------------------------------------------------------

#ifndef __P9_PM_CALLOUT
#define __P9_PM_CALLOUT

//------------------------------------------------------------------------------
// Includes
// ------------------------------------------------------------------------------
#include <fapi2.H>
#include <p9_pm_recovery_ffdc_defines.H>

enum RasAction
{
    NO_CALLOUT           =   0,
    FW_CALLOUT           =   1,
    CORE_CALLOUT         =   2,
    PROC_CHIP_CALLOUT    =   3,
    MEMORY_CALLOUT       =   4,
};

// function pointer typedef definition for HWP call support
typedef fapi2::ReturnCode (*p9_pm_callout_FP_t) (
    void* i_pHomer,
    const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_procChipTgt,
    fapi2::buffer <uint32_t>& o_deadCores,
    std::vector < StopErrLogSectn >& o_ffdcList,
    RasAction&   o_rasAction );

extern "C"
{
    /// @brief      analyzes Phyp generated dead core vector for clean up and guard.
    /// @param[in]  i_pHomerBase        points to base of HOMER buffer.
    /// @param[in]  i_procChipTgt       fapi2 target for P9 chip.
    /// @param[out] o_deadCores         bit vector of dead cores which needs to be runtime deconfigured
    /// @param[out] o_ffdcList          points to a list of STOP FFDC summary sub-sections.
    /// @param[out] o_rasAction         RAS action specified for FW.
    /// @return     fapi2 return code.
    /// @note       caller is expected to pass an empty vector. HWP will
    ///             populate a vector with a list pointers to various parts of
    ///             STOP Recovery FFDC summary. Highest priority sub-section
    ///             is pushed to vector first followed by sub-section of lower
    ///             priority. Bit vector is left aligned. Bits to be considered
    ///             are bit0 to bit 23. If bit 0 is set, it means Core0 is
    ///             marked dead by Phyp.
    ///

    fapi2::ReturnCode p9_pm_callout(
        void* i_pHomerBase,
        const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_procChipTgt,
        fapi2::buffer <uint32_t>& o_deadCores,
        std::vector < StopErrLogSectn >& o_ffdcList,
        RasAction&   o_rasAction  );
}
#endif //__P9_PM_CALLOUT
<|endoftext|>


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

#include <thrift/lib/cpp2/transport/rocket/test/util/TestServiceMock.h>

namespace testutil {
namespace testservice {

using namespace apache::thrift;

class LeakDetector {
 public:
  class InternalClass {};

  LeakDetector()
      : internal_(std::make_shared<testing::StrictMock<InternalClass>>()) {
    ++instanceCount();
  }

  LeakDetector(const LeakDetector& oth) : internal_(oth.internal_) {
    ++instanceCount();
  }

  LeakDetector& operator=(const LeakDetector& oth) {
    internal_ = oth.internal_;
    return *this;
  }

  virtual ~LeakDetector() { --instanceCount(); }

  std::shared_ptr<testing::StrictMock<InternalClass>> internal_;

  static int32_t getInstanceCount() { return instanceCount(); }

 protected:
  static std::atomic_int& instanceCount() {
    static std::atomic_int instanceCount{0};
    return instanceCount;
  }
};

int32_t TestStreamServiceMock::echo(int32_t value) {
  return value;
}

ServerStream<int32_t> TestStreamServiceMock::range(int32_t from, int32_t to) {
  auto [stream, publisher] = ServerStream<int32_t>::createPublisher();
  for (; from < to; ++from) {
    publisher.next(from);
  }
  std::move(publisher).complete();
  return std::move(stream);
}

ServerStream<std::string> TestStreamServiceMock::buffers(int32_t count) {
  auto [stream, publisher] = ServerStream<std::string>::createPublisher();
  for (int i = 0; i < count; i++) {
    publisher.next(std::string(1024, 'x'));
  }
  std::move(publisher).complete();
  return std::move(stream);
}

ServerStream<std::string> TestStreamServiceMock::customBuffers(
    int32_t count, int32_t size) {
  auto [stream, publisher] = ServerStream<std::string>::createPublisher();
  for (int i = 0; i < count; i++) {
    publisher.next(std::string(size, 'x'));
  }
  std::move(publisher).complete();
  return std::move(stream);
}

ServerStream<int32_t> TestStreamServiceMock::slowRange(
    int32_t from, int32_t to, int32_t millis) {
  auto [stream, publisher] = ServerStream<int32_t>::createPublisher();
  auto eb = folly::getEventBase();
  std::shared_ptr<std::function<void(decltype(publisher), int32_t)>> schedule =
      std::make_shared<std::function<void(decltype(publisher), int32_t)>>();
  *schedule =
      [=,
       schedule =
           std::weak_ptr<std::function<void(decltype(publisher), int32_t)>>(
               schedule)](auto publisher, int32_t from) {
        publisher.next(from);
        if (++from < to) {
          folly::futures::sleep(std::chrono::milliseconds(millis))
              .via(eb)
              .thenValue([=,
                          publisher = std::move(publisher),
                          schedule = schedule.lock()](auto) mutable {
                (*schedule)(std::move(publisher), from);
              });
        } else {
          std::move(publisher).complete();
        }
      };
  folly::futures::sleep(std::chrono::milliseconds(millis))
      .via(eb)
      .thenValue([=, publisher = std::move(publisher)](auto) mutable {
        (*schedule)(std::move(publisher), from);
      });
  return std::move(stream);
}

ServerStream<int32_t> TestStreamServiceMock::slowCancellation() {
  class Slow {
   public:
    ~Slow() {
      /* sleep override */
      std::this_thread::sleep_for(std::chrono::milliseconds{10});
    }
  };
#if FOLLY_HAS_COROUTINES
  return folly::coro::co_invoke(
      [slow = std::make_unique<Slow>()]()
          -> folly::coro::AsyncGenerator<int32_t&&> {
        LOG(FATAL) << "Should not be called";
      });
#else
  return ServerStream<int32_t>::createEmpty();
#endif
}

ResponseAndServerStream<int32_t, int32_t> TestStreamServiceMock::leakCheck(
    int32_t from, int32_t to) {
#if FOLLY_HAS_COROUTINES
  auto stream = folly::coro::co_invoke(
      [=,
       detector = LeakDetector()]() -> folly::coro::AsyncGenerator<int32_t&&> {
        for (int i = from; i < to; ++i) {
          co_yield std::move(i);
        }
      });
#else
  auto stream = ServerStream<int32_t>::createEmpty();
#endif
  return {LeakDetector::getInstanceCount(), std::move(stream)};
}

ResponseAndServerStream<int32_t, int32_t>
TestStreamServiceMock::leakCheckWithSleep(
    int32_t from, int32_t to, int32_t sleepMs) {
  std::this_thread::sleep_for(std::chrono::milliseconds{sleepMs});
  return leakCheck(from, to);
}

int32_t TestStreamServiceMock::instanceCount() {
  return LeakDetector::getInstanceCount();
}

ServerStream<Message> TestStreamServiceMock::returnNullptr() {
  return ServerStream<Message>::createEmpty();
}

ResponseAndServerStream<int, Message> TestStreamServiceMock::throwError() {
  throw Error();
}

apache::thrift::ResponseAndServerStream<int32_t, int32_t>
TestStreamServiceMock::sleepWithResponse(int32_t timeMs) {
  /* sleep override */
  std::this_thread::sleep_for(std::chrono::milliseconds(timeMs));
  return {
      1, ([]() -> folly::coro::AsyncGenerator<int32_t&&> { co_yield 1; })()};
}

apache::thrift::ServerStream<int32_t>
TestStreamServiceMock::sleepWithoutResponse(int32_t timeMs) {
  return std::move(sleepWithResponse(timeMs).stream);
}

apache::thrift::ResponseAndServerStream<int32_t, int32_t>
TestStreamServiceMock::streamServerSlow() {
#if FOLLY_HAS_COROUTINES
  auto stream = folly::coro::co_invoke(
      [=,
       detector = LeakDetector()]() -> folly::coro::AsyncGenerator<int32_t&&> {
        co_await folly::futures::sleep(std::chrono::milliseconds(1000));
        while (true) {
          co_yield 1;
        }
      });
#else
  auto stream = ServerStream<int32_t>::createEmpty();
#endif
  return {1, std::move(stream)};
}

void TestStreamServiceMock::sendMessage(
    int32_t messageId, bool complete, bool error) {
  if (!messages_) {
    throw std::runtime_error("First call registerToMessages");
  }
  if (messageId > 0) {
    messages_->next(messageId);
  }
  if (complete) {
    std::move(*messages_).complete();
    messages_.reset();
  } else if (error) {
    std::move(*messages_).complete({std::runtime_error("error")});
    messages_.reset();
  }
}

apache::thrift::ServerStream<int32_t>
TestStreamServiceMock::registerToMessages() {
  auto streamAndPublisher = ServerStream<int32_t>::createPublisher();
  messages_ = std::make_unique<apache::thrift::ServerStreamPublisher<int32_t>>(
      std::move(streamAndPublisher.second));
  return std::move(streamAndPublisher.first);
}

apache::thrift::ServerStream<Message> TestStreamServiceMock::streamThrows(
    int32_t whichEx) {
  if (whichEx == 0) {
    SecondEx ex;
    ex.errCode_ref() = 0;
    throw ex;
  }

  auto streamAndPublisher = ServerStream<Message>::createPublisher();
  if (whichEx == 1) {
    FirstEx ex;
    ex.errMsg_ref() = "FirstEx";
    ex.errCode_ref() = 1;
    std::move(streamAndPublisher.second).complete(ex);
  } else if (whichEx == 2) {
    SecondEx ex;
    ex.errCode_ref() = 2;
    std::move(streamAndPublisher.second).complete(ex);
  } else {
    std::move(streamAndPublisher.second)
        .complete(std::runtime_error("random error"));
  }
  return std::move(streamAndPublisher.first);
}

apache::thrift::ResponseAndServerStream<int32_t, Message>
TestStreamServiceMock::responseAndStreamThrows(int32_t whichEx) {
  return {1, streamThrows(whichEx)};
}

apache::thrift::ServerStream<int32_t> TestStreamServiceMock::requestWithBlob(
    std::unique_ptr<folly::IOBuf>) {
  return apache::thrift::ServerStream<int32_t>::createEmpty();
}

void TestStreamServiceMock::async_eb_leakCallback(
    std::unique_ptr<apache::thrift::HandlerCallback<
        apache::thrift::ServerStream<int32_t>>>) {}

void TestStreamServiceMock::async_eb_orderRequestStream(
    std::unique_ptr<apache::thrift::HandlerCallback<
        apache::thrift::ResponseAndServerStream<int32_t, int32_t>>> cb) {
#if FOLLY_HAS_COROUTINES
  auto stream = folly::coro::co_invoke(
      [eb = cb->getEventBase()]() -> folly::coro::AsyncGenerator<int32_t&&> {
        eb->checkIsInEventBaseThread();
        co_return;
      });
#else
  auto stream = ServerStream<int32_t>::createEmpty();
#endif
  cb->result({++order_, std::move(stream)});
}

void TestStreamServiceMock::async_eb_orderRequestResponse(
    std::unique_ptr<apache::thrift::HandlerCallback<int32_t>> cb) {
  cb->result(++order_);
}

apache::thrift::ServerStream<int32_t>
TestStreamServiceMock::leakPublisherCheck() {
  auto [stream, publisher] = ServerStream<int32_t>::createPublisher(
      [this, detector = LeakDetector()]() { publisher_ = nullptr; });
  publisher_ =
      std::make_unique<ServerStreamPublisher<int32_t>>(std::move(publisher));
  // let task expire timeout triggered
  std::this_thread::sleep_for(std::chrono::milliseconds{100});
  return std::move(stream);
}

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

#include <thrift/lib/cpp2/transport/rocket/test/util/TestServiceMock.h>

namespace testutil {
namespace testservice {

using namespace apache::thrift;

class LeakDetector {
 public:
  class InternalClass {};

  LeakDetector()
      : internal_(std::make_shared<testing::StrictMock<InternalClass>>()) {
    ++instanceCount();
  }

  LeakDetector(const LeakDetector& oth) : internal_(oth.internal_) {
    ++instanceCount();
  }

  LeakDetector& operator=(const LeakDetector& oth) {
    internal_ = oth.internal_;
    return *this;
  }

  virtual ~LeakDetector() { --instanceCount(); }

  std::shared_ptr<testing::StrictMock<InternalClass>> internal_;

  static int32_t getInstanceCount() { return instanceCount(); }

 protected:
  static std::atomic_int& instanceCount() {
    static std::atomic_int instanceCount{0};
    return instanceCount;
  }
};

int32_t TestStreamServiceMock::echo(int32_t value) {
  return value;
}

ServerStream<int32_t> TestStreamServiceMock::range(int32_t from, int32_t to) {
#if FOLLY_HAS_COROUTINES
  return folly::coro::co_invoke(
      [from, to]() mutable -> folly::coro::AsyncGenerator<int32_t&&> {
        for (; from < to; ++from) {
          co_yield int(from);
        }
      });
#else
  auto [stream, publisher] = ServerStream<int32_t>::createPublisher();
  for (; from < to; ++from) {
    publisher.next(from);
  }
  std::move(publisher).complete();
  return std::move(stream);
#endif
}

ServerStream<std::string> TestStreamServiceMock::buffers(int32_t count) {
  auto [stream, publisher] = ServerStream<std::string>::createPublisher();
  for (int i = 0; i < count; i++) {
    publisher.next(std::string(1024, 'x'));
  }
  std::move(publisher).complete();
  return std::move(stream);
}

ServerStream<std::string> TestStreamServiceMock::customBuffers(
    int32_t count, int32_t size) {
  auto [stream, publisher] = ServerStream<std::string>::createPublisher();
  for (int i = 0; i < count; i++) {
    publisher.next(std::string(size, 'x'));
  }
  std::move(publisher).complete();
  return std::move(stream);
}

ServerStream<int32_t> TestStreamServiceMock::slowRange(
    int32_t from, int32_t to, int32_t millis) {
  auto [stream, publisher] = ServerStream<int32_t>::createPublisher();
  auto eb = folly::getEventBase();
  std::shared_ptr<std::function<void(decltype(publisher), int32_t)>> schedule =
      std::make_shared<std::function<void(decltype(publisher), int32_t)>>();
  *schedule =
      [=,
       schedule =
           std::weak_ptr<std::function<void(decltype(publisher), int32_t)>>(
               schedule)](auto publisher, int32_t from) {
        publisher.next(from);
        if (++from < to) {
          folly::futures::sleep(std::chrono::milliseconds(millis))
              .via(eb)
              .thenValue([=,
                          publisher = std::move(publisher),
                          schedule = schedule.lock()](auto) mutable {
                (*schedule)(std::move(publisher), from);
              });
        } else {
          std::move(publisher).complete();
        }
      };
  folly::futures::sleep(std::chrono::milliseconds(millis))
      .via(eb)
      .thenValue([=, publisher = std::move(publisher)](auto) mutable {
        (*schedule)(std::move(publisher), from);
      });
  return std::move(stream);
}

ServerStream<int32_t> TestStreamServiceMock::slowCancellation() {
  class Slow {
   public:
    ~Slow() {
      /* sleep override */
      std::this_thread::sleep_for(std::chrono::milliseconds{10});
    }
  };
#if FOLLY_HAS_COROUTINES
  return folly::coro::co_invoke(
      [slow = std::make_unique<Slow>()]()
          -> folly::coro::AsyncGenerator<int32_t&&> {
        LOG(FATAL) << "Should not be called";
      });
#else
  return ServerStream<int32_t>::createEmpty();
#endif
}

ResponseAndServerStream<int32_t, int32_t> TestStreamServiceMock::leakCheck(
    int32_t from, int32_t to) {
#if FOLLY_HAS_COROUTINES
  auto stream = folly::coro::co_invoke(
      [=,
       detector = LeakDetector()]() -> folly::coro::AsyncGenerator<int32_t&&> {
        for (int i = from; i < to; ++i) {
          co_yield std::move(i);
        }
      });
#else
  auto stream = ServerStream<int32_t>::createEmpty();
#endif
  return {LeakDetector::getInstanceCount(), std::move(stream)};
}

ResponseAndServerStream<int32_t, int32_t>
TestStreamServiceMock::leakCheckWithSleep(
    int32_t from, int32_t to, int32_t sleepMs) {
  std::this_thread::sleep_for(std::chrono::milliseconds{sleepMs});
  return leakCheck(from, to);
}

int32_t TestStreamServiceMock::instanceCount() {
  return LeakDetector::getInstanceCount();
}

ServerStream<Message> TestStreamServiceMock::returnNullptr() {
  return ServerStream<Message>::createEmpty();
}

ResponseAndServerStream<int, Message> TestStreamServiceMock::throwError() {
  throw Error();
}

apache::thrift::ResponseAndServerStream<int32_t, int32_t>
TestStreamServiceMock::sleepWithResponse(int32_t timeMs) {
  /* sleep override */
  std::this_thread::sleep_for(std::chrono::milliseconds(timeMs));
  return {
      1, ([]() -> folly::coro::AsyncGenerator<int32_t&&> { co_yield 1; })()};
}

apache::thrift::ServerStream<int32_t>
TestStreamServiceMock::sleepWithoutResponse(int32_t timeMs) {
  return std::move(sleepWithResponse(timeMs).stream);
}

apache::thrift::ResponseAndServerStream<int32_t, int32_t>
TestStreamServiceMock::streamServerSlow() {
#if FOLLY_HAS_COROUTINES
  auto stream = folly::coro::co_invoke(
      [=,
       detector = LeakDetector()]() -> folly::coro::AsyncGenerator<int32_t&&> {
        co_await folly::futures::sleep(std::chrono::milliseconds(1000));
        while (true) {
          co_yield 1;
        }
      });
#else
  auto stream = ServerStream<int32_t>::createEmpty();
#endif
  return {1, std::move(stream)};
}

void TestStreamServiceMock::sendMessage(
    int32_t messageId, bool complete, bool error) {
  if (!messages_) {
    throw std::runtime_error("First call registerToMessages");
  }
  if (messageId > 0) {
    messages_->next(messageId);
  }
  if (complete) {
    std::move(*messages_).complete();
    messages_.reset();
  } else if (error) {
    std::move(*messages_).complete({std::runtime_error("error")});
    messages_.reset();
  }
}

apache::thrift::ServerStream<int32_t>
TestStreamServiceMock::registerToMessages() {
  auto streamAndPublisher = ServerStream<int32_t>::createPublisher();
  messages_ = std::make_unique<apache::thrift::ServerStreamPublisher<int32_t>>(
      std::move(streamAndPublisher.second));
  return std::move(streamAndPublisher.first);
}

apache::thrift::ServerStream<Message> TestStreamServiceMock::streamThrows(
    int32_t whichEx) {
  if (whichEx == 0) {
    SecondEx ex;
    ex.errCode_ref() = 0;
    throw ex;
  }

  auto streamAndPublisher = ServerStream<Message>::createPublisher();
  if (whichEx == 1) {
    FirstEx ex;
    ex.errMsg_ref() = "FirstEx";
    ex.errCode_ref() = 1;
    std::move(streamAndPublisher.second).complete(ex);
  } else if (whichEx == 2) {
    SecondEx ex;
    ex.errCode_ref() = 2;
    std::move(streamAndPublisher.second).complete(ex);
  } else {
    std::move(streamAndPublisher.second)
        .complete(std::runtime_error("random error"));
  }
  return std::move(streamAndPublisher.first);
}

apache::thrift::ResponseAndServerStream<int32_t, Message>
TestStreamServiceMock::responseAndStreamThrows(int32_t whichEx) {
  return {1, streamThrows(whichEx)};
}

apache::thrift::ServerStream<int32_t> TestStreamServiceMock::requestWithBlob(
    std::unique_ptr<folly::IOBuf>) {
  return apache::thrift::ServerStream<int32_t>::createEmpty();
}

void TestStreamServiceMock::async_eb_leakCallback(
    std::unique_ptr<apache::thrift::HandlerCallback<
        apache::thrift::ServerStream<int32_t>>>) {}

void TestStreamServiceMock::async_eb_orderRequestStream(
    std::unique_ptr<apache::thrift::HandlerCallback<
        apache::thrift::ResponseAndServerStream<int32_t, int32_t>>> cb) {
#if FOLLY_HAS_COROUTINES
  auto stream = folly::coro::co_invoke(
      [eb = cb->getEventBase()]() -> folly::coro::AsyncGenerator<int32_t&&> {
        eb->checkIsInEventBaseThread();
        co_return;
      });
#else
  auto stream = ServerStream<int32_t>::createEmpty();
#endif
  cb->result({++order_, std::move(stream)});
}

void TestStreamServiceMock::async_eb_orderRequestResponse(
    std::unique_ptr<apache::thrift::HandlerCallback<int32_t>> cb) {
  cb->result(++order_);
}

apache::thrift::ServerStream<int32_t>
TestStreamServiceMock::leakPublisherCheck() {
  auto [stream, publisher] = ServerStream<int32_t>::createPublisher(
      [this, detector = LeakDetector()]() { publisher_ = nullptr; });
  publisher_ =
      std::make_unique<ServerStreamPublisher<int32_t>>(std::move(publisher));
  // let task expire timeout triggered
  std::this_thread::sleep_for(std::chrono::milliseconds{100});
  return std::move(stream);
}

} // namespace testservice
} // namespace testutil
<|endoftext|>


<commit_before>/*
  -----------------------------------------------------------------------------
  This source file is part of OGRE
  (Object-oriented Graphics Rendering Engine)
  For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2014 Torus Knot Software Ltd

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  -----------------------------------------------------------------------------
*/

#include "OgreVulkanHardwareBuffer.h"

#include "OgreVulkanDevice.h"
#include "OgreVulkanUtils.h"

namespace Ogre
{
    VulkanHardwareBuffer::VulkanHardwareBuffer(uint32 target,
        size_t sizeBytes, Usage usage, bool useShadowBuffer, VulkanDevice *device ) :
        HardwareBuffer(usage, false, useShadowBuffer),
        mDevice( device )
    {
        mSizeInBytes = sizeBytes;

        mLastFrameUsed = 0;//mVaoManager->getFrameCount() - mVaoManager->getDynamicBufferMultiplier();
        mLastFrameGpuWrote = mLastFrameUsed;

        VkBufferCreateInfo bufferCi = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
        bufferCi.size = sizeBytes;
        bufferCi.usage = target;

        if((mUsage & HBU_CPU_ONLY) == 0)
            bufferCi.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;

        OGRE_VK_CHECK(vkCreateBuffer(mDevice->mDevice, &bufferCi, 0, &mBuffer));

        VkMemoryRequirements buffMemRequirements;
        vkGetBufferMemoryRequirements( mDevice->mDevice, mBuffer, &buffMemRequirements );

        VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
        memAllocInfo.allocationSize = buffMemRequirements.size;

        uint32 reqFlags = 0;
        if(usage != HBU_GPU_ONLY)
            reqFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
        else
            reqFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

        const auto& memProperties = mDevice->mDeviceMemoryProperties;
        for(; memAllocInfo.memoryTypeIndex < memProperties.memoryTypeCount; memAllocInfo.memoryTypeIndex++)
        {
            if ((memProperties.memoryTypes[memAllocInfo.memoryTypeIndex].propertyFlags & reqFlags) == reqFlags)
            {
                break;
            }
        }

        OGRE_VK_CHECK(vkAllocateMemory(mDevice->mDevice, &memAllocInfo, NULL, &mMemory));
        OGRE_VK_CHECK(vkBindBufferMemory(mDevice->mDevice, mBuffer, mMemory, 0));

        if(mUsage == HBU_GPU_ONLY)
        {
            mShadowBuffer.reset(new VulkanHardwareBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sizeBytes, HBU_CPU_ONLY, false, mDevice));
        }
    }

    VulkanHardwareBuffer::~VulkanHardwareBuffer()
    {
        // delay until we are sure this buffer is no longer in use
        mDevice->mGraphicsQueue.queueForDeletion(mBuffer, mMemory);
    }

    void VulkanHardwareBuffer::_notifyDeviceStalled()
    {
        mLastFrameUsed = 0;//mVaoManager->getFrameCount() - mVaoManager->getDynamicBufferMultiplier();
        mLastFrameGpuWrote = mLastFrameUsed;
    }

    VkBuffer VulkanHardwareBuffer::getVkBuffer()
    {
        mLastFrameUsed = 0;//mVaoManager->getFrameCount();
        //if( !mDiscardBuffer )
        {
            //outOffset = mBuffer.mInternalBufferStart;
            return mBuffer;
        }
    }

    void* VulkanHardwareBuffer::lockImpl(size_t offset, size_t length, LockOptions options)
    {
        void *retPtr = 0;

        const uint32 currentFrame       = 0;//mVaoManager->getFrameCount();
        const uint32 bufferMultiplier   = 1;//mVaoManager->getDynamicBufferMultiplier();

        if( options == HardwareBuffer::HBL_READ_ONLY )
        {
            if( currentFrame - mLastFrameGpuWrote < bufferMultiplier )
                mDevice->stall();
        }
        else if( options != HardwareBuffer::HBL_NO_OVERWRITE && 0 )
        {
            if( currentFrame - mLastFrameUsed < bufferMultiplier )
            {
                LogManager::getSingleton().logMessage(
                    "PERFORMANCE WARNING: locking to a non-HBU_WRITE_ONLY or "
                    "non-HBU_DISCARDABLE for other than reading is slow/stalling." );

                mDevice->stall();
            }
        }

        if(mShadowBuffer)
            return mShadowBuffer->lock(offset, length, options);

        OGRE_VK_CHECK(vkMapMemory(mDevice->mDevice, mMemory, 0, mSizeInBytes, 0, &retPtr));
        return static_cast<uint8*>(retPtr) + offset;
    }

    void VulkanHardwareBuffer::unlockImpl()
    {
        OgreAssert(!mShadowBuffer, "should be handled by _updateFromShadow");
        vkUnmapMemory( mDevice->mDevice, mMemory );
    }

    void VulkanHardwareBuffer::readData( size_t offset, size_t length, void *pDest )
    {
        // just use memcpy
        HardwareBufferLockGuard thisLock(this, offset, length, HBL_READ_ONLY);
        memcpy(pDest, thisLock.pData, length);
    }

    void VulkanHardwareBuffer::writeData(size_t offset, size_t length, const void* pSource,
                                               bool discardWholeBuffer)
    {
        // just use memcpy
        HardwareBufferLockGuard thisLock(this, offset, length, discardWholeBuffer ? HBL_DISCARD : HBL_NORMAL);
        memcpy(thisLock.pData, pSource, length);
    }

    void VulkanHardwareBuffer::_updateFromShadow()
    {
        if(mShadowBuffer && mShadowUpdated && !mSuppressHardwareUpdate)
		{
			bool discardWholeBuffer = mLockStart == 0 && mLockSize == mSizeInBytes;
			copyData(*mShadowBuffer, mLockStart, mLockStart, mLockSize, discardWholeBuffer);
			mShadowUpdated = false;
        }
    }

    void VulkanHardwareBuffer::copyData( HardwareBuffer& _srcBuffer, size_t srcOffset,
                                               size_t dstOffset, size_t length, bool discardWholeBuffer )
    {
        auto srcBuffer = dynamic_cast<VulkanHardwareBuffer*>( &_srcBuffer );
        if( srcBuffer && (mUsage & HBU_CPU_ONLY) == 0)
        {
            mDevice->mGraphicsQueue.getCopyEncoderV1Buffer( false );

            VkBuffer srcBuf = srcBuffer->getVkBuffer();
            VkBufferCopy region = {srcOffset, dstOffset};
            region.size = alignToNextMultiple( length, 4u );
            vkCmdCopyBuffer( mDevice->mGraphicsQueue.mCurrentCmdBuffer, srcBuf, mBuffer, 1u, &region );

            //if( this->mDiscardBuffer )
            //    mDevice->stall();
        }
        else
        {
            const void* srcData = _srcBuffer.lock(srcOffset, length, HBL_READ_ONLY);
            writeData(dstOffset, length, srcData, discardWholeBuffer);
            _srcBuffer.unlock();
        }
    }
}
<commit_msg>Vulkan: HardwareBuffer - aligning copy region is wrong<commit_after>/*
  -----------------------------------------------------------------------------
  This source file is part of OGRE
  (Object-oriented Graphics Rendering Engine)
  For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2014 Torus Knot Software Ltd

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  -----------------------------------------------------------------------------
*/

#include "OgreVulkanHardwareBuffer.h"

#include "OgreVulkanDevice.h"
#include "OgreVulkanUtils.h"

namespace Ogre
{
    VulkanHardwareBuffer::VulkanHardwareBuffer(uint32 target,
        size_t sizeBytes, Usage usage, bool useShadowBuffer, VulkanDevice *device ) :
        HardwareBuffer(usage, false, useShadowBuffer),
        mDevice( device )
    {
        mSizeInBytes = sizeBytes;

        mLastFrameUsed = 0;//mVaoManager->getFrameCount() - mVaoManager->getDynamicBufferMultiplier();
        mLastFrameGpuWrote = mLastFrameUsed;

        VkBufferCreateInfo bufferCi = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
        bufferCi.size = sizeBytes;
        bufferCi.usage = target;

        if((mUsage & HBU_CPU_ONLY) == 0)
            bufferCi.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;

        OGRE_VK_CHECK(vkCreateBuffer(mDevice->mDevice, &bufferCi, 0, &mBuffer));

        VkMemoryRequirements buffMemRequirements;
        vkGetBufferMemoryRequirements( mDevice->mDevice, mBuffer, &buffMemRequirements );

        VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
        memAllocInfo.allocationSize = buffMemRequirements.size;

        uint32 reqFlags = 0;
        if(usage != HBU_GPU_ONLY)
            reqFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
        else
            reqFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

        const auto& memProperties = mDevice->mDeviceMemoryProperties;
        for(; memAllocInfo.memoryTypeIndex < memProperties.memoryTypeCount; memAllocInfo.memoryTypeIndex++)
        {
            if ((memProperties.memoryTypes[memAllocInfo.memoryTypeIndex].propertyFlags & reqFlags) == reqFlags)
            {
                break;
            }
        }

        OGRE_VK_CHECK(vkAllocateMemory(mDevice->mDevice, &memAllocInfo, NULL, &mMemory));
        OGRE_VK_CHECK(vkBindBufferMemory(mDevice->mDevice, mBuffer, mMemory, 0));

        if(mUsage == HBU_GPU_ONLY)
        {
            mShadowBuffer.reset(new VulkanHardwareBuffer(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, sizeBytes, HBU_CPU_ONLY, false, mDevice));
        }
    }

    VulkanHardwareBuffer::~VulkanHardwareBuffer()
    {
        // delay until we are sure this buffer is no longer in use
        mDevice->mGraphicsQueue.queueForDeletion(mBuffer, mMemory);
    }

    void VulkanHardwareBuffer::_notifyDeviceStalled()
    {
        mLastFrameUsed = 0;//mVaoManager->getFrameCount() - mVaoManager->getDynamicBufferMultiplier();
        mLastFrameGpuWrote = mLastFrameUsed;
    }

    VkBuffer VulkanHardwareBuffer::getVkBuffer()
    {
        mLastFrameUsed = 0;//mVaoManager->getFrameCount();
        //if( !mDiscardBuffer )
        {
            //outOffset = mBuffer.mInternalBufferStart;
            return mBuffer;
        }
    }

    void* VulkanHardwareBuffer::lockImpl(size_t offset, size_t length, LockOptions options)
    {
        void *retPtr = 0;

        const uint32 currentFrame       = 0;//mVaoManager->getFrameCount();
        const uint32 bufferMultiplier   = 1;//mVaoManager->getDynamicBufferMultiplier();

        if( options == HardwareBuffer::HBL_READ_ONLY )
        {
            if( currentFrame - mLastFrameGpuWrote < bufferMultiplier )
                mDevice->stall();
        }
        else if( options != HardwareBuffer::HBL_NO_OVERWRITE && 0 )
        {
            if( currentFrame - mLastFrameUsed < bufferMultiplier )
            {
                LogManager::getSingleton().logMessage(
                    "PERFORMANCE WARNING: locking to a non-HBU_WRITE_ONLY or "
                    "non-HBU_DISCARDABLE for other than reading is slow/stalling." );

                mDevice->stall();
            }
        }

        if(mShadowBuffer)
            return mShadowBuffer->lock(offset, length, options);

        OGRE_VK_CHECK(vkMapMemory(mDevice->mDevice, mMemory, 0, mSizeInBytes, 0, &retPtr));
        return static_cast<uint8*>(retPtr) + offset;
    }

    void VulkanHardwareBuffer::unlockImpl()
    {
        OgreAssert(!mShadowBuffer, "should be handled by _updateFromShadow");
        vkUnmapMemory( mDevice->mDevice, mMemory );
    }

    void VulkanHardwareBuffer::readData( size_t offset, size_t length, void *pDest )
    {
        // just use memcpy
        HardwareBufferLockGuard thisLock(this, offset, length, HBL_READ_ONLY);
        memcpy(pDest, thisLock.pData, length);
    }

    void VulkanHardwareBuffer::writeData(size_t offset, size_t length, const void* pSource,
                                               bool discardWholeBuffer)
    {
        // just use memcpy
        HardwareBufferLockGuard thisLock(this, offset, length, discardWholeBuffer ? HBL_DISCARD : HBL_NORMAL);
        memcpy(thisLock.pData, pSource, length);
    }

    void VulkanHardwareBuffer::_updateFromShadow()
    {
        if(mShadowBuffer && mShadowUpdated && !mSuppressHardwareUpdate)
		{
			bool discardWholeBuffer = mLockStart == 0 && mLockSize == mSizeInBytes;
			copyData(*mShadowBuffer, mLockStart, mLockStart, mLockSize, discardWholeBuffer);
			mShadowUpdated = false;
        }
    }

    void VulkanHardwareBuffer::copyData( HardwareBuffer& _srcBuffer, size_t srcOffset,
                                               size_t dstOffset, size_t length, bool discardWholeBuffer )
    {
        auto srcBuffer = dynamic_cast<VulkanHardwareBuffer*>( &_srcBuffer );
        if( srcBuffer && (mUsage & HBU_CPU_ONLY) == 0)
        {
            mDevice->mGraphicsQueue.getCopyEncoderV1Buffer( false );

            VkBuffer srcBuf = srcBuffer->getVkBuffer();
            VkBufferCopy region = {srcOffset, dstOffset, length};
            vkCmdCopyBuffer( mDevice->mGraphicsQueue.mCurrentCmdBuffer, srcBuf, mBuffer, 1u, &region );

            //if( this->mDiscardBuffer )
            //    mDevice->stall();
        }
        else
        {
            const void* srcData = _srcBuffer.lock(srcOffset, length, HBL_READ_ONLY);
            writeData(dstOffset, length, srcData, discardWholeBuffer);
            _srcBuffer.unlock();
        }
    }
}
<|endoftext|>


<commit_before>/*
    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

#include "ch.h"
#include "hal.h"

#include "blink.h"
#include "usbconfig.h"
#include "printf.h"

#include "encoder.h"

namespace {
    const systime_t loop_time = MS2ST(100); /* serial loop at 10 Hz */
    Encoder encoder(&GPTD3, /* CH1, CH2 connected to PC6, PC7 and enabled by board.h */
            {PAL_NOLINE, /* no index channel */
             1024, /* counts per revolution */
             EncoderConfig::filter_t::CAPTURE_64}); /* 64 * 42 MHz (TIM3 on APB1) = 1.52 us for valid edge */

    const uint32_t pwm_freq = 1000000; /* 1 MHz PWM clock frequency */
    const pwmcnt_t pwm_period = 1000; /* period is 10000 ticks (1 kHz) */

    PWMConfig pwmcfg = {
        pwm_freq,
        pwm_period,
        nullptr,
        {
            {PWM_OUTPUT_ACTIVE_HIGH, nullptr},
            {PWM_OUTPUT_DISABLED, nullptr},
            {PWM_OUTPUT_DISABLED, nullptr},
            {PWM_OUTPUT_DISABLED, nullptr}
        },
        0,
        0
    };

    /*
     * GPT8 configuration. This timer is used as trigger to start the PWM signals.
     */
    void start_pwm_cb(GPTDriver* gptp);
    const GPTConfig gptcfg8 = {
        frequency:    pwm_freq,
        callback:     start_pwm_cb,
        cr2:          0,
        dier:         0U
    };

    /*
     * Start PWM drivers 1/4 period out of phase to simulate an encoder.
     */
    void start_pwm_cb(GPTDriver* gptp) {
        (void)gptp;

        chSysLockFromISR();
        gptStartOneShotI(&GPTD8, pwm_period/4);
        if (PWMD1.state == PWM_STOP) {
            PWMD1.config = &pwmcfg;
            PWMD1.period = pwmcfg.period;
            pwm_lld_start(&PWMD1);
            PWMD1.enabled = 0;
            PWMD1.state = PWM_READY;
        } else if (PWMD4.state == PWM_STOP) {
            PWMD4.config = &pwmcfg;
            PWMD4.period = pwmcfg.period;
            pwm_lld_start(&PWMD4);
            PWMD4.enabled = 0;
            PWMD4.state = PWM_READY;
        } else {
            pwmEnableChannelI(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 5000));
            pwmEnableChannelI(&PWMD4, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD4, 5000));
            gptStopTimerI(&GPTD8);
        }
        chSysUnlockFromISR();
    }
} // namespace

static THD_WORKING_AREA(waSerialThread, 256);
static THD_FUNCTION(SerialThread, arg) {
    (void)arg;
    chRegSetThreadName("serial");

    /*
     * Initializes a serial-over-USB CDC driver.
     */
    sduObjectInit(&SDU1);
    sduStart(&SDU1, &serusbcfg);

    /*
     * Activates the USB driver and then the USB bus pull-up on D+.
     * Note, a delay is inserted in order to not have to disconnect the cable
     * after a reset.
     */
    board_usb_lld_disconnect_bus();   //usbDisconnectBus(serusbcfg.usbp);
    chThdSleepMilliseconds(1500);
    usbStart(serusbcfg.usbp, &usbcfg);
    board_usb_lld_connect_bus();      //usbConnectBus(serusbcfg.usbp);

    while (true) {
        if (SDU1.config->usbp->state == USB_ACTIVE) {
            printf("%d\r\n", encoder.count());
        }
        chThdSleep(loop_time);
    }
}

/*
 * Application entry point.
 */
int main(void) {

    /*
     * System initializations.
     * - HAL initialization, this also initializes the configured device drivers
     *   and performs the board-specific initializations.
     * - Kernel initialization, the main() function becomes a thread and the
     *   RTOS is active.
     */
    halInit();
    chSysInit();

    /*
     * Initializes the encoder driver 3 on pins PC6, PC7
     * Initializes the PWM driver 1 and 4, starting PWMD4 1/4 period after PWMD1.
     * GPIOA8 is the PWMD1 output.
     * GPIOB6 is the PWMD4 output.
     */
    encoder.start();
    gptStart(&GPTD8, &gptcfg8);
    gptStartOneShot(&GPTD8, gptcfg8.frequency);

    palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(1));
    palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(2));

    /*
     * Creates the LED blink and USB serial threads.
     */
    chBlinkThreadCreateStatic(NORMALPRIO-1);
    chThdCreateStatic(waSerialThread, sizeof(waSerialThread), NORMALPRIO+1,
            SerialThread, nullptr);

    /*
     * Normal main() thread activity. In this demo it does nothing.
     */
    while (true) {
        chThdSleep(MS2ST(500));
    }
}
<commit_msg>Add missing EncoderConfig field<commit_after>/*
    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

#include "ch.h"
#include "hal.h"

#include "blink.h"
#include "usbconfig.h"
#include "printf.h"

#include "encoder.h"

namespace {
    const systime_t loop_time = MS2ST(100); /* serial loop at 10 Hz */
    Encoder encoder(&GPTD3, /* CH1, CH2 connected to PC6, PC7 and enabled by board.h */
            {PAL_NOLINE, /* no index channel */
             1024, /* counts per revolution */
             EncoderConfig::filter_t::CAPTURE_64, /* 64 * 42 MHz (TIM3 on APB1) = 1.52 us for valid edge */
             0});

    const uint32_t pwm_freq = 1000000; /* 1 MHz PWM clock frequency */
    const pwmcnt_t pwm_period = 1000; /* period is 10000 ticks (1 kHz) */

    PWMConfig pwmcfg = {
        pwm_freq,
        pwm_period,
        nullptr,
        {
            {PWM_OUTPUT_ACTIVE_HIGH, nullptr},
            {PWM_OUTPUT_DISABLED, nullptr},
            {PWM_OUTPUT_DISABLED, nullptr},
            {PWM_OUTPUT_DISABLED, nullptr}
        },
        0,
        0
    };

    /*
     * GPT8 configuration. This timer is used as trigger to start the PWM signals.
     */
    void start_pwm_cb(GPTDriver* gptp);
    const GPTConfig gptcfg8 = {
        frequency:    pwm_freq,
        callback:     start_pwm_cb,
        cr2:          0,
        dier:         0U
    };

    /*
     * Start PWM drivers 1/4 period out of phase to simulate an encoder.
     */
    void start_pwm_cb(GPTDriver* gptp) {
        (void)gptp;

        chSysLockFromISR();
        gptStartOneShotI(&GPTD8, pwm_period/4);
        if (PWMD1.state == PWM_STOP) {
            PWMD1.config = &pwmcfg;
            PWMD1.period = pwmcfg.period;
            pwm_lld_start(&PWMD1);
            PWMD1.enabled = 0;
            PWMD1.state = PWM_READY;
        } else if (PWMD4.state == PWM_STOP) {
            PWMD4.config = &pwmcfg;
            PWMD4.period = pwmcfg.period;
            pwm_lld_start(&PWMD4);
            PWMD4.enabled = 0;
            PWMD4.state = PWM_READY;
        } else {
            pwmEnableChannelI(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 5000));
            pwmEnableChannelI(&PWMD4, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD4, 5000));
            gptStopTimerI(&GPTD8);
        }
        chSysUnlockFromISR();
    }
} // namespace

static THD_WORKING_AREA(waSerialThread, 256);
static THD_FUNCTION(SerialThread, arg) {
    (void)arg;
    chRegSetThreadName("serial");

    /*
     * Initializes a serial-over-USB CDC driver.
     */
    sduObjectInit(&SDU1);
    sduStart(&SDU1, &serusbcfg);

    /*
     * Activates the USB driver and then the USB bus pull-up on D+.
     * Note, a delay is inserted in order to not have to disconnect the cable
     * after a reset.
     */
    board_usb_lld_disconnect_bus();   //usbDisconnectBus(serusbcfg.usbp);
    chThdSleepMilliseconds(1500);
    usbStart(serusbcfg.usbp, &usbcfg);
    board_usb_lld_connect_bus();      //usbConnectBus(serusbcfg.usbp);

    while (true) {
        if (SDU1.config->usbp->state == USB_ACTIVE) {
            printf("%d\r\n", encoder.count());
        }
        chThdSleep(loop_time);
    }
}

/*
 * Application entry point.
 */
int main(void) {

    /*
     * System initializations.
     * - HAL initialization, this also initializes the configured device drivers
     *   and performs the board-specific initializations.
     * - Kernel initialization, the main() function becomes a thread and the
     *   RTOS is active.
     */
    halInit();
    chSysInit();

    /*
     * Initializes the encoder driver 3 on pins PC6, PC7
     * Initializes the PWM driver 1 and 4, starting PWMD4 1/4 period after PWMD1.
     * GPIOA8 is the PWMD1 output.
     * GPIOB6 is the PWMD4 output.
     */
    encoder.start();
    gptStart(&GPTD8, &gptcfg8);
    gptStartOneShot(&GPTD8, gptcfg8.frequency);

    palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(1));
    palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(2));

    /*
     * Creates the LED blink and USB serial threads.
     */
    chBlinkThreadCreateStatic(NORMALPRIO-1);
    chThdCreateStatic(waSerialThread, sizeof(waSerialThread), NORMALPRIO+1,
            SerialThread, nullptr);

    /*
     * Normal main() thread activity. In this demo it does nothing.
     */
    while (true) {
        chThdSleep(MS2ST(500));
    }
}
<|endoftext|>


<commit_before>/* ====================================================================
 * Copyright (c) 2007-2010 Open Source Applications Foundation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * ====================================================================
 */

#include "common.h"
#include "structmember.h"

#include "bases.h"
#include "charset.h"
#include "macros.h"

/* CharsetDetector */

class t_charsetdetector : public _wrapper {
public:
    UCharsetDetector *object;
    PyObject *text;
};

static int t_charsetdetector_init(t_charsetdetector *self,
                                  PyObject *args, PyObject *kwds);
static PyObject *t_charsetdetector_setText(t_charsetdetector *self,
                                           PyObject *arg);
static PyObject *t_charsetdetector_setDeclaredEncoding(t_charsetdetector *self,
                                                       PyObject *arg);
static PyObject *t_charsetdetector_detect(t_charsetdetector *self);
static PyObject *t_charsetdetector_detectAll(t_charsetdetector *self);
static PyObject *t_charsetdetector_enableInputFilter(t_charsetdetector *self,
                                                     PyObject *arg);
static PyObject *t_charsetdetector_isInputFilterEnabled(t_charsetdetector *self);
static PyObject *t_charsetdetector_getAllDetectableCharsets(t_charsetdetector *self);

static PyMethodDef t_charsetdetector_methods[] = {
    DECLARE_METHOD(t_charsetdetector, setText, METH_O),
    DECLARE_METHOD(t_charsetdetector, setDeclaredEncoding, METH_O),
    DECLARE_METHOD(t_charsetdetector, detect, METH_NOARGS),
    DECLARE_METHOD(t_charsetdetector, detectAll, METH_NOARGS),
    DECLARE_METHOD(t_charsetdetector, enableInputFilter, METH_O),
    DECLARE_METHOD(t_charsetdetector, isInputFilterEnabled, METH_NOARGS),
    DECLARE_METHOD(t_charsetdetector, getAllDetectableCharsets, METH_NOARGS),
    { NULL, NULL, 0, NULL }
};

static void t_charsetdetector_dealloc(t_charsetdetector *self)
{
    if (self->object)
    {
        ucsdet_close(self->object);
        self->object = NULL;
    }
    Py_CLEAR(self->text);

    Py_TYPE(self)->tp_free((PyObject *) self);
}

DECLARE_STRUCT(CharsetDetector, t_charsetdetector, UCharsetDetector,
               t_charsetdetector_init, t_charsetdetector_dealloc);

/* CharsetMatch */

class t_charsetmatch : public _wrapper {
public:
    UCharsetMatch *object;
    t_charsetdetector *detector;
};

static PyObject *t_charsetmatch_getName(t_charsetmatch *self);
static PyObject *t_charsetmatch_getConfidence(t_charsetmatch *self);
static PyObject *t_charsetmatch_getLanguage(t_charsetmatch *self);

static PyMethodDef t_charsetmatch_methods[] = {
    DECLARE_METHOD(t_charsetmatch, getName, METH_NOARGS),
    DECLARE_METHOD(t_charsetmatch, getConfidence, METH_NOARGS),
    DECLARE_METHOD(t_charsetmatch, getLanguage, METH_NOARGS),
    { NULL, NULL, 0, NULL }
};

static void t_charsetmatch_dealloc(t_charsetmatch *self)
{
    if (self->object) /* not owned */
    {
        self->object = NULL;
        Py_CLEAR(self->detector);
    }

    Py_TYPE(self)->tp_free((PyObject *) self);
}

DECLARE_STRUCT(CharsetMatch, t_charsetmatch, UCharsetMatch,
               abstract_init, t_charsetmatch_dealloc);

/* CharsetDetector */

static int t_charsetdetector_init(t_charsetdetector *self,
                                  PyObject *args, PyObject *kwds)
{
    const char *text;
    charsArg encoding;
    int textSize;

    switch (PyTuple_Size(args)) {
      case 0:
        INT_STATUS_CALL(self->object = ucsdet_open(&status));
        break;

      case 1:
        if (!parseArgs(args, "k", &text, &textSize))
        {
            INT_STATUS_CALL(self->object = ucsdet_open(&status));
            INT_STATUS_CALL(ucsdet_setText(self->object, text, textSize,
                                           &status));
            self->text = PyTuple_GetItem(args, 0);
            Py_INCREF(self->text);
            break;
        }
        PyErr_SetArgsError((PyObject *) self, "__init__", args);
        return -1;

      case 2:
        if (!parseArgs(args, "kn", &text, &textSize, &encoding))
        {
            INT_STATUS_CALL(self->object = ucsdet_open(&status));
            INT_STATUS_CALL(ucsdet_setText(self->object, text,
                                           textSize, &status));
            INT_STATUS_CALL(ucsdet_setDeclaredEncoding(self->object, encoding,
                                                       -1, &status));
            self->text = PyTuple_GetItem(args, 0);
            Py_INCREF(self->text);
            break;
        }
        PyErr_SetArgsError((PyObject *) self, "__init__", args);
        return -1;

      default:
        PyErr_SetArgsError((PyObject *) self, "__init__", args);
        return -1;
    }

    if (self->object)
        return 0;

    return -1;
}

static PyObject *t_charsetdetector_setText(t_charsetdetector *self,
                                           PyObject *arg)
{
    const char *text;
    int size;

    if (!parseArg(arg, "k", &text, &size))
    {
        /* ref'd */
        STATUS_CALL(ucsdet_setText(self->object, text, size, &status));

        Py_INCREF(arg);
        Py_XDECREF(self->text);
        self->text = arg;

        Py_RETURN_NONE;
    }

    return PyErr_SetArgsError((PyObject *) self, "setText", arg);
}

static PyObject *t_charsetdetector_setDeclaredEncoding(t_charsetdetector *self,
                                                       PyObject *arg)
{
    const char *encoding;
    int size;

    if (!parseArg(arg, "k", &encoding, &size))
    {
        /* copied */
        STATUS_CALL(ucsdet_setDeclaredEncoding(self->object, encoding, size,
                                               &status));
        Py_RETURN_NONE;
    }

    return PyErr_SetArgsError((PyObject *) self, "setDeclaredEncoding", arg);
}

static PyObject *t_charsetdetector_detect(t_charsetdetector *self)
{
    const UCharsetMatch *match;
    PyObject *result;

    STATUS_CALL(match = ucsdet_detect(self->object, &status));

    result = wrap_CharsetMatch((UCharsetMatch *) match, 0);
    if (result)
    {
        ((t_charsetmatch *) result)->detector = self;
        Py_INCREF(self);
    }

    return result;
}

static PyObject *t_charsetdetector_detectAll(t_charsetdetector *self)
{
    const UCharsetMatch **matches;
    PyObject *result;
    int found = 0;

    STATUS_CALL(matches = ucsdet_detectAll(self->object, &found, &status));
    result = PyTuple_New(found);

    for (int i = 0; i < found; i++) {
        const UCharsetMatch *match = matches[i];
        PyObject *m = wrap_CharsetMatch((UCharsetMatch *) match, 0);

        if (m)
        {
            ((t_charsetmatch *) m)->detector = self;
            Py_INCREF(self);
            PyTuple_SET_ITEM(result, i, m);
        }
        else
        {
            Py_DECREF(result);
            return NULL;
        }
    }

    return result;
}

static PyObject *t_charsetdetector_enableInputFilter(t_charsetdetector *self,
                                                     PyObject *arg)
{
    UBool filter;

    if (!parseArg(arg, "B", &filter))
    {
        UBool previous = ucsdet_enableInputFilter(self->object, filter);
        Py_RETURN_BOOL(previous);
    }

    return PyErr_SetArgsError((PyObject *) self, "enableInputFilter", arg);
}

static PyObject *t_charsetdetector_isInputFilterEnabled(t_charsetdetector *self)
{
    UBool filter = ucsdet_isInputFilterEnabled(self->object);
    Py_RETURN_BOOL(filter);
}

static PyObject *t_charsetdetector_getAllDetectableCharsets(t_charsetdetector *self)
{
    UEnumeration *_charsets;

    STATUS_CALL(_charsets = ucsdet_getAllDetectableCharsets(self->object,
                                                            &status));

    return wrap_StringEnumeration(new UStringEnumeration(_charsets), T_OWNED);
}

/* CharsetMatch */

static PyObject *t_charsetmatch_getName(t_charsetmatch *self)
{
    const char *name;

    STATUS_CALL(name = ucsdet_getName(self->object, &status));
    return PyString_FromString(name);
}

static PyObject *t_charsetmatch_getConfidence(t_charsetmatch *self)
{
    int confidence;

    STATUS_CALL(confidence = ucsdet_getConfidence(self->object, &status));
    return PyInt_FromLong(confidence);
}

static PyObject *t_charsetmatch_getLanguage(t_charsetmatch *self)
{
    const char *language;

    STATUS_CALL(language = ucsdet_getLanguage(self->object, &status));
    return PyString_FromString(language);
}

static PyObject *t_charsetmatch_str(t_charsetmatch *self)
{
    if (self->detector && self->detector->text)
    {
        UErrorCode status = U_ZERO_ERROR;
        int size = PyBytes_GET_SIZE(self->detector->text);
        UChar *buf = new UChar[size];
        PyObject *u;

        if (!buf)
        {
            PyErr_SetNone(PyExc_MemoryError);
            return NULL;
        }

        size = ucsdet_getUChars(self->object, buf, size, &status);
        if (U_FAILURE(status))
        {
            delete buf;
            return ICUException(status).reportError();
        }

        u = PyUnicode_FromUnicodeString(buf, size);
        delete buf;

        return u;
    }

    return PyUnicode_FromUnicode(NULL, 0);
}

void _init_charset(PyObject *m)
{
    CharsetMatchType_.tp_str = (reprfunc) t_charsetmatch_str;

    INSTALL_STRUCT(CharsetDetector, m);
    INSTALL_STRUCT(CharsetMatch, m);
}
<commit_msg>fixed new[]/delete mismatch<commit_after>/* ====================================================================
 * Copyright (c) 2007-2010 Open Source Applications Foundation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * ====================================================================
 */

#include "common.h"
#include "structmember.h"

#include "bases.h"
#include "charset.h"
#include "macros.h"

/* CharsetDetector */

class t_charsetdetector : public _wrapper {
public:
    UCharsetDetector *object;
    PyObject *text;
};

static int t_charsetdetector_init(t_charsetdetector *self,
                                  PyObject *args, PyObject *kwds);
static PyObject *t_charsetdetector_setText(t_charsetdetector *self,
                                           PyObject *arg);
static PyObject *t_charsetdetector_setDeclaredEncoding(t_charsetdetector *self,
                                                       PyObject *arg);
static PyObject *t_charsetdetector_detect(t_charsetdetector *self);
static PyObject *t_charsetdetector_detectAll(t_charsetdetector *self);
static PyObject *t_charsetdetector_enableInputFilter(t_charsetdetector *self,
                                                     PyObject *arg);
static PyObject *t_charsetdetector_isInputFilterEnabled(t_charsetdetector *self);
static PyObject *t_charsetdetector_getAllDetectableCharsets(t_charsetdetector *self);

static PyMethodDef t_charsetdetector_methods[] = {
    DECLARE_METHOD(t_charsetdetector, setText, METH_O),
    DECLARE_METHOD(t_charsetdetector, setDeclaredEncoding, METH_O),
    DECLARE_METHOD(t_charsetdetector, detect, METH_NOARGS),
    DECLARE_METHOD(t_charsetdetector, detectAll, METH_NOARGS),
    DECLARE_METHOD(t_charsetdetector, enableInputFilter, METH_O),
    DECLARE_METHOD(t_charsetdetector, isInputFilterEnabled, METH_NOARGS),
    DECLARE_METHOD(t_charsetdetector, getAllDetectableCharsets, METH_NOARGS),
    { NULL, NULL, 0, NULL }
};

static void t_charsetdetector_dealloc(t_charsetdetector *self)
{
    if (self->object)
    {
        ucsdet_close(self->object);
        self->object = NULL;
    }
    Py_CLEAR(self->text);

    Py_TYPE(self)->tp_free((PyObject *) self);
}

DECLARE_STRUCT(CharsetDetector, t_charsetdetector, UCharsetDetector,
               t_charsetdetector_init, t_charsetdetector_dealloc);

/* CharsetMatch */

class t_charsetmatch : public _wrapper {
public:
    UCharsetMatch *object;
    t_charsetdetector *detector;
};

static PyObject *t_charsetmatch_getName(t_charsetmatch *self);
static PyObject *t_charsetmatch_getConfidence(t_charsetmatch *self);
static PyObject *t_charsetmatch_getLanguage(t_charsetmatch *self);

static PyMethodDef t_charsetmatch_methods[] = {
    DECLARE_METHOD(t_charsetmatch, getName, METH_NOARGS),
    DECLARE_METHOD(t_charsetmatch, getConfidence, METH_NOARGS),
    DECLARE_METHOD(t_charsetmatch, getLanguage, METH_NOARGS),
    { NULL, NULL, 0, NULL }
};

static void t_charsetmatch_dealloc(t_charsetmatch *self)
{
    if (self->object) /* not owned */
    {
        self->object = NULL;
        Py_CLEAR(self->detector);
    }

    Py_TYPE(self)->tp_free((PyObject *) self);
}

DECLARE_STRUCT(CharsetMatch, t_charsetmatch, UCharsetMatch,
               abstract_init, t_charsetmatch_dealloc);

/* CharsetDetector */

static int t_charsetdetector_init(t_charsetdetector *self,
                                  PyObject *args, PyObject *kwds)
{
    const char *text;
    charsArg encoding;
    int textSize;

    switch (PyTuple_Size(args)) {
      case 0:
        INT_STATUS_CALL(self->object = ucsdet_open(&status));
        break;

      case 1:
        if (!parseArgs(args, "k", &text, &textSize))
        {
            INT_STATUS_CALL(self->object = ucsdet_open(&status));
            INT_STATUS_CALL(ucsdet_setText(self->object, text, textSize,
                                           &status));
            self->text = PyTuple_GetItem(args, 0);
            Py_INCREF(self->text);
            break;
        }
        PyErr_SetArgsError((PyObject *) self, "__init__", args);
        return -1;

      case 2:
        if (!parseArgs(args, "kn", &text, &textSize, &encoding))
        {
            INT_STATUS_CALL(self->object = ucsdet_open(&status));
            INT_STATUS_CALL(ucsdet_setText(self->object, text,
                                           textSize, &status));
            INT_STATUS_CALL(ucsdet_setDeclaredEncoding(self->object, encoding,
                                                       -1, &status));
            self->text = PyTuple_GetItem(args, 0);
            Py_INCREF(self->text);
            break;
        }
        PyErr_SetArgsError((PyObject *) self, "__init__", args);
        return -1;

      default:
        PyErr_SetArgsError((PyObject *) self, "__init__", args);
        return -1;
    }

    if (self->object)
        return 0;

    return -1;
}

static PyObject *t_charsetdetector_setText(t_charsetdetector *self,
                                           PyObject *arg)
{
    const char *text;
    int size;

    if (!parseArg(arg, "k", &text, &size))
    {
        /* ref'd */
        STATUS_CALL(ucsdet_setText(self->object, text, size, &status));

        Py_INCREF(arg);
        Py_XDECREF(self->text);
        self->text = arg;

        Py_RETURN_NONE;
    }

    return PyErr_SetArgsError((PyObject *) self, "setText", arg);
}

static PyObject *t_charsetdetector_setDeclaredEncoding(t_charsetdetector *self,
                                                       PyObject *arg)
{
    const char *encoding;
    int size;

    if (!parseArg(arg, "k", &encoding, &size))
    {
        /* copied */
        STATUS_CALL(ucsdet_setDeclaredEncoding(self->object, encoding, size,
                                               &status));
        Py_RETURN_NONE;
    }

    return PyErr_SetArgsError((PyObject *) self, "setDeclaredEncoding", arg);
}

static PyObject *t_charsetdetector_detect(t_charsetdetector *self)
{
    const UCharsetMatch *match;
    PyObject *result;

    STATUS_CALL(match = ucsdet_detect(self->object, &status));

    result = wrap_CharsetMatch((UCharsetMatch *) match, 0);
    if (result)
    {
        ((t_charsetmatch *) result)->detector = self;
        Py_INCREF(self);
    }

    return result;
}

static PyObject *t_charsetdetector_detectAll(t_charsetdetector *self)
{
    const UCharsetMatch **matches;
    PyObject *result;
    int found = 0;

    STATUS_CALL(matches = ucsdet_detectAll(self->object, &found, &status));
    result = PyTuple_New(found);

    for (int i = 0; i < found; i++) {
        const UCharsetMatch *match = matches[i];
        PyObject *m = wrap_CharsetMatch((UCharsetMatch *) match, 0);

        if (m)
        {
            ((t_charsetmatch *) m)->detector = self;
            Py_INCREF(self);
            PyTuple_SET_ITEM(result, i, m);
        }
        else
        {
            Py_DECREF(result);
            return NULL;
        }
    }

    return result;
}

static PyObject *t_charsetdetector_enableInputFilter(t_charsetdetector *self,
                                                     PyObject *arg)
{
    UBool filter;

    if (!parseArg(arg, "B", &filter))
    {
        UBool previous = ucsdet_enableInputFilter(self->object, filter);
        Py_RETURN_BOOL(previous);
    }

    return PyErr_SetArgsError((PyObject *) self, "enableInputFilter", arg);
}

static PyObject *t_charsetdetector_isInputFilterEnabled(t_charsetdetector *self)
{
    UBool filter = ucsdet_isInputFilterEnabled(self->object);
    Py_RETURN_BOOL(filter);
}

static PyObject *t_charsetdetector_getAllDetectableCharsets(t_charsetdetector *self)
{
    UEnumeration *_charsets;

    STATUS_CALL(_charsets = ucsdet_getAllDetectableCharsets(self->object,
                                                            &status));

    return wrap_StringEnumeration(new UStringEnumeration(_charsets), T_OWNED);
}

/* CharsetMatch */

static PyObject *t_charsetmatch_getName(t_charsetmatch *self)
{
    const char *name;

    STATUS_CALL(name = ucsdet_getName(self->object, &status));
    return PyString_FromString(name);
}

static PyObject *t_charsetmatch_getConfidence(t_charsetmatch *self)
{
    int confidence;

    STATUS_CALL(confidence = ucsdet_getConfidence(self->object, &status));
    return PyInt_FromLong(confidence);
}

static PyObject *t_charsetmatch_getLanguage(t_charsetmatch *self)
{
    const char *language;

    STATUS_CALL(language = ucsdet_getLanguage(self->object, &status));
    return PyString_FromString(language);
}

static PyObject *t_charsetmatch_str(t_charsetmatch *self)
{
    if (self->detector && self->detector->text)
    {
        UErrorCode status = U_ZERO_ERROR;
        int size = PyBytes_GET_SIZE(self->detector->text);
        UChar *buf = new UChar[size];
        PyObject *u;

        if (!buf)
        {
            PyErr_SetNone(PyExc_MemoryError);
            return NULL;
        }

        size = ucsdet_getUChars(self->object, buf, size, &status);
        if (U_FAILURE(status))
        {
            delete[] buf;
            return ICUException(status).reportError();
        }

        u = PyUnicode_FromUnicodeString(buf, size);
        delete[] buf;

        return u;
    }

    return PyUnicode_FromUnicode(NULL, 0);
}

void _init_charset(PyObject *m)
{
    CharsetMatchType_.tp_str = (reprfunc) t_charsetmatch_str;

    INSTALL_STRUCT(CharsetDetector, m);
    INSTALL_STRUCT(CharsetMatch, m);
}
<|endoftext|>


<commit_before>#include "dialog.h"
#include "ui_dialog.h"
#include <QFileDialog>
#include <QRegExp>
#include <QTemporaryFile>
#include <QMessageBox>
#include "LuminaUtils.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    desktopType="application"; //default value
}

Dialog::~Dialog()
{
    delete ui;
}

//Inform the user that required input parameters are missing
void Dialog::MissingInputs()
{
    qDebug() << "We cannot continue without a desktop file !!!";
    QMessageBox::critical(this, tr("Error"), tr("The application requires inputs: <-application>|<-link> desktopfile") );
    exit(1);
}

//Initialise the layout of the screen.
void Dialog::Initialise(QString param)
{
    //"application" is the default layout.

    //in case of "link", several objects are no required
    if (param.startsWith("-link")) {
        ui->cbRunInTerminal->setVisible(false);
        ui->cbStartupNotification->setVisible(false);
        ui->lCommand->setVisible(false);
        ui->pbCommand->setVisible(false);
        ui->label_3->setVisible(false);
        ui->label_6->setVisible(false);
        ui->label_4->setText("URL"); //we use the WorkingDir boxes for URL
        desktopType="link";
    }
}

//load the desktop file or the required template
void Dialog::LoadDesktopFile(QString input)
{
    //if we have "-" as 1st char, it means that this is not a desktop file, but a parameter
    desktopFileName = input;
    if (input.startsWith("-")) {
        QMessageBox::critical(this,tr("Error"),tr("The filename cannot start with a \"-\"."));
        exit(1);
    }

    //if proposed file does not exist, than we will create one based on the templates
    //TODO: have a config directory to store templates
    if (!QFile::exists(input)) {
        if (desktopType=="link") { QFile::copy("./desktop-link.template", desktopFileName);}
        else { QFile::copy("./desktop-app.template", desktopFileName);}
    }

    //use the standard LXDG object and load the desktop file
    bool ok = false;
    DF = LXDG::loadDesktopFile(desktopFileName, ok);
    if( ok ) {
        if ((DF.type == XDGDesktop::LINK) && (desktopType!="link" )) {
            //we open a desktop type "link" but it was not mentionned by parameters
            Dialog::Initialise("-link");
        }
        ui->lName->setText(DF.name);
        ui->lComment->setText(DF.comment);
        ui->lCommand->setText(DF.exec);
        //in case of "link" desktop, we populate the correct content in lWorkingDir
        if (desktopType=="link") {
            ui->lWorkingDir->setText(DF.url);
        } else {
            ui->lWorkingDir->setText(DF.path);
        }
        if (DF.startupNotify) ui->cbStartupNotification->setChecked(true); else ui->cbStartupNotification->setChecked(false);
        if (DF.useTerminal) ui->cbRunInTerminal->setChecked(true); else ui->cbRunInTerminal->setChecked(false);
        iconFileName="";
        ui->pbIcon->setIcon(QPixmap(DF.icon));
    } else {
        QMessageBox::critical(this, tr("Error"), tr("Problem to read the desktop file called:") + desktopFileName );
        exit(1);
    }
}

void Dialog::on_pbCommand_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open command"), "~", tr("All Files (*)"));
    ui->lCommand->setText(fileName);
}

void Dialog::on_pbWorkingDir_clicked()
{
    QFileDialog::Options options = QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly;
    QString directory = QFileDialog::getExistingDirectory(this,
                                    tr("Working Directory"),
                                    ui->lWorkingDir->text(),
                                    options);
    ui->lWorkingDir->setText(directory);
}

//this function is just like a regexp.
//we just change the required lines and we don't touch to the rest of the file and copy it back.
void textReplace(QString &origin, QString from, QString to, QString topic)
{
    if (!from.isEmpty()) {
        //TODO RegExp with ^ to detect begining of the line is not working
        origin.replace(QRegExp("\n" + topic + "\\s*=\\s*" + from + "\n",Qt::CaseInsensitive),"\n" + topic + "=" + to + "\n");
    } else {
        //TODO: check if last char in \n. If not add it
        origin.append(topic + "=" + to + "\n");
    }
}

//we save the changes to the destination file
void Dialog::on_pbApply_clicked()
{

    QByteArray fileData;
    QFile file(desktopFileName);
    if (file.open(QFile::ReadWrite)) {
        QString from,to;
        fileData = file.readAll();
        QString text(fileData);

        QString desktopTypeVal="Application";
        if (DF.type == XDGDesktop::APP) { desktopTypeVal="Application"; }
        else if (DF.type == XDGDesktop::LINK) { desktopTypeVal="Link"; }
        else if (DF.type == XDGDesktop::DIR) { desktopTypeVal="Dir"; }
        textReplace(text, desktopTypeVal, desktopType, "Type");

        if (ui->lName->isModified()) { textReplace(text, DF.name, ui->lName->text(), "Name");}
        if (ui->lComment->isModified()) { textReplace(text, DF.comment, ui->lComment->text(), "Comment");}
        if (ui->lCommand->isModified()) { textReplace(text, DF.exec, ui->lCommand->text(),"Exec");}
        if (desktopType=="link") {
            //incase of "link" layout WorkingDir is corresponding to the URL
            if (ui->lWorkingDir->isModified()) { textReplace(text, DF.url, ui->lWorkingDir->text(),"URL");}
        } else {
            if (ui->lWorkingDir->isModified()) { textReplace(text, DF.path, ui->lWorkingDir->text(),"Path");}
        }
        if (ui->cbStartupNotification->isChecked() != DF.startupNotify) {
            if (DF.startupNotify) {from="true"; to="false";} else {from="false"; to="true";}
            textReplace(text, from, to,"StartupNotify");
        }
        if (ui->cbRunInTerminal->isChecked() != DF.useTerminal) {
            if (DF.useTerminal) {from="true"; to="false";} else {from="false"; to="true";}
            textReplace(text, from, to,"Terminal");
        }
        if (!iconFileName.isEmpty()) {
            from=DF.icon;
            to=iconFileName;
            textReplace(text, from, to,"Icon");
        }

        file.seek(0);
        file.write(text.toUtf8());

        file.resize(file.pos());//remove possible trailing lines

        file.close();

        //hack required to update the icon on the desktop
        //maybe the solution would be to have a QFileSystemWatcher (in AppLauncherPlugin.cpp)
        //on files instead of ~/Desktop
            QTemporaryFile tempFile ;
            tempFile.setAutoRemove(false);
            tempFile.open();
            tempFile.close();
            qDebug() << "temp file:" << tempFile.fileName();

            //TODO: capture errors
            QString cmd = "mv";
            cmd = cmd + " " + desktopFileName + " " + tempFile.fileName();
            qDebug() << "cmd:" << cmd;
            int ret = LUtils::runCmd(cmd);

            cmd = "mv";
            cmd = cmd + " " + tempFile.fileName() + " " + desktopFileName;
            qDebug() << "cmd:" << cmd;
            ret = LUtils::runCmd(cmd);
    }
}

void Dialog::on_pbIcon_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open command"), "~", tr("Image Files (*.png *.jpg *.bmp)"));
    qDebug() << "icon:" << fileName;
    ui->pbIcon->setIcon(QPixmap(fileName));
    iconFileName=fileName;
}
<commit_msg>automatically display the correct folder when clicking on Icon button<commit_after>#include "dialog.h"
#include "ui_dialog.h"
#include <QFileDialog>
#include <QRegExp>
#include <QTemporaryFile>
#include <QMessageBox>
#include "LuminaUtils.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    desktopType="application"; //default value
}

Dialog::~Dialog()
{
    delete ui;
}

//Inform the user that required input parameters are missing
void Dialog::MissingInputs()
{
    qDebug() << "We cannot continue without a desktop file !!!";
    QMessageBox::critical(this, tr("Error"), tr("The application requires inputs: <-application>|<-link> desktopfile") );
    exit(1);
}

//Initialise the layout of the screen.
void Dialog::Initialise(QString param)
{
    //"application" is the default layout.

    //in case of "link", several objects are no required
    if (param.startsWith("-link")) {
        ui->cbRunInTerminal->setVisible(false);
        ui->cbStartupNotification->setVisible(false);
        ui->lCommand->setVisible(false);
        ui->pbCommand->setVisible(false);
        ui->label_3->setVisible(false);
        ui->label_6->setVisible(false);
        ui->label_4->setText("URL"); //we use the WorkingDir boxes for URL
        desktopType="link";
    }
}

//load the desktop file or the required template
void Dialog::LoadDesktopFile(QString input)
{
    //if we have "-" as 1st char, it means that this is not a desktop file, but a parameter
    desktopFileName = input;
    if (input.startsWith("-")) {
        QMessageBox::critical(this,tr("Error"),tr("The filename cannot start with a \"-\"."));
        exit(1);
    }

    //if proposed file does not exist, than we will create one based on the templates
    //TODO: have a config directory to store templates
    if (!QFile::exists(input)) {
        if (desktopType=="link") { QFile::copy("./desktop-link.template", desktopFileName);}
        else { QFile::copy("./desktop-app.template", desktopFileName);}
    }

    //use the standard LXDG object and load the desktop file
    bool ok = false;
    DF = LXDG::loadDesktopFile(desktopFileName, ok);
    if( ok ) {
        if ((DF.type == XDGDesktop::LINK) && (desktopType!="link" )) {
            //we open a desktop type "link" but it was not mentionned by parameters
            Dialog::Initialise("-link");
        }
        ui->lName->setText(DF.name);
        ui->lComment->setText(DF.comment);
        ui->lCommand->setText(DF.exec);
        //in case of "link" desktop, we populate the correct content in lWorkingDir
        if (desktopType=="link") {
            ui->lWorkingDir->setText(DF.url);
        } else {
            ui->lWorkingDir->setText(DF.path);
        }
        if (DF.startupNotify) ui->cbStartupNotification->setChecked(true); else ui->cbStartupNotification->setChecked(false);
        if (DF.useTerminal) ui->cbRunInTerminal->setChecked(true); else ui->cbRunInTerminal->setChecked(false);
        iconFileName="";
        ui->pbIcon->setIcon(QPixmap(DF.icon));
    } else {
        QMessageBox::critical(this, tr("Error"), tr("Problem to read the desktop file called:") + desktopFileName );
        exit(1);
    }
}

void Dialog::on_pbCommand_clicked()
{
    //TODO: detect Command directory

    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open command"), "~", tr("All Files (*)"));
    ui->lCommand->setText(fileName);
}

void Dialog::on_pbWorkingDir_clicked()
{
    QFileDialog::Options options = QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly;
    QString directory = QFileDialog::getExistingDirectory(this,
                                    tr("Working Directory"),
                                    ui->lWorkingDir->text(),
                                    options);
    ui->lWorkingDir->setText(directory);
}

//this function is just like a regexp.
//we just change the required lines and we don't touch to the rest of the file and copy it back.
void textReplace(QString &origin, QString from, QString to, QString topic)
{
    if (!from.isEmpty()) {
        //TODO RegExp with ^ to detect begining of the line is not working
        origin.replace(QRegExp("\n" + topic + "\\s*=\\s*" + from + "\n",Qt::CaseInsensitive),"\n" + topic + "=" + to + "\n");
    } else {
        //TODO: check if last char in \n. If not add it
        origin.append(topic + "=" + to + "\n");
    }
}

//we save the changes to the destination file
void Dialog::on_pbApply_clicked()
{

    QByteArray fileData;
    QFile file(desktopFileName);
    if (file.open(QFile::ReadWrite)) {
        QString from,to;
        fileData = file.readAll();
        QString text(fileData);

        QString desktopTypeVal="Application";
        if (DF.type == XDGDesktop::APP) { desktopTypeVal="Application"; }
        else if (DF.type == XDGDesktop::LINK) { desktopTypeVal="Link"; }
        else if (DF.type == XDGDesktop::DIR) { desktopTypeVal="Dir"; }
        textReplace(text, desktopTypeVal, desktopType, "Type");

        if (ui->lName->isModified()) { textReplace(text, DF.name, ui->lName->text(), "Name");}
        if (ui->lComment->isModified()) { textReplace(text, DF.comment, ui->lComment->text(), "Comment");}
        if (ui->lCommand->isModified()) { textReplace(text, DF.exec, ui->lCommand->text(),"Exec");}
        if (desktopType=="link") {
            //incase of "link" layout WorkingDir is corresponding to the URL
            if (ui->lWorkingDir->isModified()) { textReplace(text, DF.url, ui->lWorkingDir->text(),"URL");}
        } else {
            if (ui->lWorkingDir->isModified()) { textReplace(text, DF.path, ui->lWorkingDir->text(),"Path");}
        }
        if (ui->cbStartupNotification->isChecked() != DF.startupNotify) {
            if (DF.startupNotify) {from="true"; to="false";} else {from="false"; to="true";}
            textReplace(text, from, to,"StartupNotify");
        }
        if (ui->cbRunInTerminal->isChecked() != DF.useTerminal) {
            if (DF.useTerminal) {from="true"; to="false";} else {from="false"; to="true";}
            textReplace(text, from, to,"Terminal");
        }
        if (!iconFileName.isEmpty()) {
            from=DF.icon;
            to=iconFileName;
            textReplace(text, from, to,"Icon");
        }

        file.seek(0);
        file.write(text.toUtf8());

        file.resize(file.pos());//remove possible trailing lines

        file.close();

        //hack required to update the icon on the desktop
        //maybe the solution would be to have a QFileSystemWatcher (in AppLauncherPlugin.cpp)
        //on files instead of ~/Desktop
            QTemporaryFile tempFile ;
            tempFile.setAutoRemove(false);
            tempFile.open();
            tempFile.close();

            //TODO: capture errors
            QString cmd = "mv";
            cmd = cmd + " " + desktopFileName + " " + tempFile.fileName();
            int ret = LUtils::runCmd(cmd);

            cmd = "mv";
            cmd = cmd + " " + tempFile.fileName() + " " + desktopFileName;
            ret = LUtils::runCmd(cmd);
    }
}

void Dialog::on_pbIcon_clicked()
{
	QString iconFolder="~";
    if (!iconFileName.isEmpty()) {
		iconFolder = iconFileName.section('/', 0, -2);
	}
	else if (!DF.icon.isEmpty()) {
			iconFolder = DF.icon.section('/', 0, -2);
	}

    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open command"), iconFolder, tr("Image Files (*.png *.jpg *.bmp)"));
    qDebug() << "icon:" << fileName;
    ui->pbIcon->setIcon(QPixmap(fileName));
    iconFileName=fileName;
}
<|endoftext|>