<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/async/ClientSinkBridge.h>

#include <folly/Overload.h>

namespace apache {
namespace thrift {
namespace detail {

// Explicitly instantiate the base of ClientSinkBridge
template class TwoWayBridge<
    ClientSinkConsumer,
    ClientMessage,
    ClientSinkBridge,
    ServerMessage,
    ClientSinkBridge>;

ClientSinkBridge::ClientSinkBridge(FirstResponseCallback* callback)
    : firstResponseCallback_(callback) {}

ClientSinkBridge::~ClientSinkBridge() {}

SinkClientCallback* ClientSinkBridge::create(FirstResponseCallback* callback) {
  return new ClientSinkBridge(callback);
}

void ClientSinkBridge::close() {
  serverClose();
  serverCallback_ = nullptr;
  evb_.reset();
  Ptr(this);
}

bool ClientSinkBridge::wait(ClientSinkConsumer* consumer) {
  return clientWait(consumer);
}

void ClientSinkBridge::push(ServerMessage&& value) {
  clientPush(std::move(value));
}

ClientSinkBridge::ClientQueue ClientSinkBridge::getMessages() {
  return clientGetMessages();
}

#if FOLLY_HAS_COROUTINES

folly::coro::Task<folly::Try<StreamPayload>> ClientSinkBridge::sink(
    folly::coro::AsyncGenerator<folly::Try<StreamPayload>&&> generator) {
  uint64_t credits = 0;
  folly::Try<StreamPayload> finalResponse;
  const auto& clientCancelToken =
      co_await folly::coro::co_current_cancellation_token;
  auto mergedToken = folly::CancellationToken::merge(
      serverCancelSource_.getToken(), clientCancelToken);

  auto waitEvent = [&]() -> folly::coro::Task<void> {
    CoroConsumer consumer;
    if (clientWait(&consumer)) {
      folly::CancellationCallback cb{
          clientCancelToken, [&]() {
            if (auto* cancelledCb = cancelClientWait()) {
              cancelledCb->canceled();
            }
          }};
      co_await consumer.wait();
    }
    auto queue = clientGetMessages();
    while (!queue.empty()) {
      auto& message = queue.front();
      folly::variant_match(
          message,
          [&](folly::Try<StreamPayload>& payload) {
            finalResponse = std::move(payload);
          },
          [&](uint64_t n) { credits += n; });
      queue.pop();
      if (finalResponse.hasValue() || finalResponse.hasException()) {
        co_return;
      }
    }
    co_return;
  };

  bool sinkComplete = false;

  while (true) {
    co_await waitEvent();
    if (finalResponse.hasValue() || finalResponse.hasException()) {
      break;
    }

    if (clientCancelToken.isCancellationRequested()) {
      if (!sinkComplete) {
        clientPush(folly::Try<apache::thrift::StreamPayload>(
            rocket::RocketException(rocket::ErrorCode::CANCELED)));
      }
      co_yield folly::coro::co_cancelled;
    }

    while (credits > 0 && !sinkComplete &&
           !serverCancelSource_.isCancellationRequested()) {
      auto item = co_await folly::coro::co_withCancellation(
          mergedToken, generator.next());
      if (serverCancelSource_.isCancellationRequested()) {
        break;
      }

      if (clientCancelToken.isCancellationRequested()) {
        clientPush(folly::Try<apache::thrift::StreamPayload>(
            rocket::RocketException(rocket::ErrorCode::CANCELED)));
        co_yield folly::coro::co_cancelled;
      }

      if (item.has_value()) {
        if ((*item).hasValue()) {
          clientPush(std::move(*item));
        } else {
          clientPush(std::move(*item));
          // AsyncGenerator who serialized and yield the exception also in
          // charge of propagating it back to user, just return empty Try
          // here.
          co_return folly::Try<StreamPayload>();
        }
      } else {
        clientPush({});
        sinkComplete = true;
        // release generator
        generator = {};
      }
      credits--;
    }
  }
  co_return std::move(finalResponse);
}

#endif

void ClientSinkBridge::cancel(folly::Try<StreamPayload> payload) {
  CHECK(payload.hasException());
  clientPush(std::move(payload));
}

// SinkClientCallback method
bool ClientSinkBridge::onFirstResponse(
    FirstResponsePayload&& firstPayload,
    folly::EventBase* evb,
    SinkServerCallback* serverCallback) {
  auto firstResponseCallback = firstResponseCallback_;
  serverCallback_ = serverCallback;
  evb_ = folly::getKeepAliveToken(evb);
  bool scheduledWait = serverWait(this);
  DCHECK(scheduledWait);
  firstResponseCallback->onFirstResponse(std::move(firstPayload), copy());
  return true;
}

void ClientSinkBridge::onFirstResponseError(folly::exception_wrapper ew) {
  firstResponseCallback_->onFirstResponseError(std::move(ew));
  close();
}

void ClientSinkBridge::onFinalResponse(StreamPayload&& payload) {
  serverPush(folly::Try<StreamPayload>(std::move(payload)));
  serverCancelSource_.requestCancellation();
  close();
}

void ClientSinkBridge::onFinalResponseError(folly::exception_wrapper ew) {
  using apache::thrift::detail::EncodedError;
  auto rex = ew.get_exception<rocket::RocketException>();
  auto payload = rex
      ? folly::Try<StreamPayload>(EncodedError(rex->moveErrorData()))
      : folly::Try<StreamPayload>(std::move(ew));
  serverPush(std::move(payload));
  serverCancelSource_.requestCancellation();
  close();
}

bool ClientSinkBridge::onSinkRequestN(uint64_t n) {
  serverPush(n);
  return true;
}

void ClientSinkBridge::resetServerCallback(SinkServerCallback& serverCallback) {
  serverCallback_ = &serverCallback;
}

void ClientSinkBridge::consume() {
  DCHECK(evb_);
  evb_->runInEventBaseThread(
      [self = copy()]() mutable { self->processServerMessages(); });
}

void ClientSinkBridge::processServerMessages() {
  if (!serverCallback_) {
    return;
  }

  do {
    for (auto messages = serverGetMessages(); !messages.empty();
         messages.pop()) {
      bool terminated = false;
      auto& payload = messages.front();
      if (payload.hasException()) {
        serverCallback_->onSinkError(std::move(payload).exception());
        terminated = true;
      } else if (payload.hasValue()) {
        terminated = !serverCallback_->onSinkNext(std::move(payload).value());
      } else {
        terminated = !serverCallback_->onSinkComplete();
      }

      if (terminated) {
        close();
        return;
      }
    }
  } while (!serverWait(this));
}

bool ClientSinkBridge::hasServerCancelled() {
  return serverCancelSource_.isCancellationRequested();
}

} // namespace detail
} // namespace thrift
} // namespace apache
<commit_msg>Tighten scopes in ClientSinkBridge::sink<commit_after>/*
 * 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/async/ClientSinkBridge.h>

#include <folly/Overload.h>

namespace apache {
namespace thrift {
namespace detail {

// Explicitly instantiate the base of ClientSinkBridge
template class TwoWayBridge<
    ClientSinkConsumer,
    ClientMessage,
    ClientSinkBridge,
    ServerMessage,
    ClientSinkBridge>;

ClientSinkBridge::ClientSinkBridge(FirstResponseCallback* callback)
    : firstResponseCallback_(callback) {}

ClientSinkBridge::~ClientSinkBridge() {}

SinkClientCallback* ClientSinkBridge::create(FirstResponseCallback* callback) {
  return new ClientSinkBridge(callback);
}

void ClientSinkBridge::close() {
  serverClose();
  serverCallback_ = nullptr;
  evb_.reset();
  Ptr(this);
}

bool ClientSinkBridge::wait(ClientSinkConsumer* consumer) {
  return clientWait(consumer);
}

void ClientSinkBridge::push(ServerMessage&& value) {
  clientPush(std::move(value));
}

ClientSinkBridge::ClientQueue ClientSinkBridge::getMessages() {
  return clientGetMessages();
}

#if FOLLY_HAS_COROUTINES

folly::coro::Task<folly::Try<StreamPayload>> ClientSinkBridge::sink(
    folly::coro::AsyncGenerator<folly::Try<StreamPayload>&&> generator) {
  uint64_t credits = 0;
  const auto& clientCancelToken =
      co_await folly::coro::co_current_cancellation_token;
  auto mergedToken = folly::CancellationToken::merge(
      serverCancelSource_.getToken(), clientCancelToken);
  bool sinkComplete = false;

  while (true) {
    if (CoroConsumer consumer; clientWait(&consumer)) {
      folly::CancellationCallback cb{
          clientCancelToken, [&]() {
            if (auto* cancelledCb = cancelClientWait()) {
              cancelledCb->canceled();
            }
          }};
      co_await consumer.wait();
    }

    for (auto queue = clientGetMessages(); !queue.empty(); queue.pop()) {
      auto& message = queue.front();
      if (auto* n = std::get_if<uint64_t>(&message)) {
        credits += *n;
      } else {
        co_return std::get<folly::Try<StreamPayload>>(std::move(message));
      }
    }

    if (clientCancelToken.isCancellationRequested()) {
      if (!sinkComplete) {
        clientPush(folly::Try<apache::thrift::StreamPayload>(
            rocket::RocketException(rocket::ErrorCode::CANCELED)));
      }
      co_yield folly::coro::co_cancelled;
    }

    while (credits > 0 && !sinkComplete &&
           !serverCancelSource_.isCancellationRequested()) {
      auto item = co_await folly::coro::co_withCancellation(
          mergedToken, generator.next());
      if (serverCancelSource_.isCancellationRequested()) {
        break;
      }

      if (clientCancelToken.isCancellationRequested()) {
        clientPush(folly::Try<apache::thrift::StreamPayload>(
            rocket::RocketException(rocket::ErrorCode::CANCELED)));
        co_yield folly::coro::co_cancelled;
      }

      if (item.has_value()) {
        if ((*item).hasValue()) {
          clientPush(std::move(*item));
        } else {
          clientPush(std::move(*item));
          // AsyncGenerator who serialized and yield the exception also in
          // charge of propagating it back to user, just return empty Try
          // here.
          co_return folly::Try<StreamPayload>();
        }
      } else {
        clientPush({});
        sinkComplete = true;
        // release generator
        generator = {};
      }
      credits--;
    }
  }
}

#endif

void ClientSinkBridge::cancel(folly::Try<StreamPayload> payload) {
  CHECK(payload.hasException());
  clientPush(std::move(payload));
}

// SinkClientCallback method
bool ClientSinkBridge::onFirstResponse(
    FirstResponsePayload&& firstPayload,
    folly::EventBase* evb,
    SinkServerCallback* serverCallback) {
  auto firstResponseCallback = firstResponseCallback_;
  serverCallback_ = serverCallback;
  evb_ = folly::getKeepAliveToken(evb);
  bool scheduledWait = serverWait(this);
  DCHECK(scheduledWait);
  firstResponseCallback->onFirstResponse(std::move(firstPayload), copy());
  return true;
}

void ClientSinkBridge::onFirstResponseError(folly::exception_wrapper ew) {
  firstResponseCallback_->onFirstResponseError(std::move(ew));
  close();
}

void ClientSinkBridge::onFinalResponse(StreamPayload&& payload) {
  serverPush(folly::Try<StreamPayload>(std::move(payload)));
  serverCancelSource_.requestCancellation();
  close();
}

void ClientSinkBridge::onFinalResponseError(folly::exception_wrapper ew) {
  using apache::thrift::detail::EncodedError;
  auto rex = ew.get_exception<rocket::RocketException>();
  auto payload = rex
      ? folly::Try<StreamPayload>(EncodedError(rex->moveErrorData()))
      : folly::Try<StreamPayload>(std::move(ew));
  serverPush(std::move(payload));
  serverCancelSource_.requestCancellation();
  close();
}

bool ClientSinkBridge::onSinkRequestN(uint64_t n) {
  serverPush(n);
  return true;
}

void ClientSinkBridge::resetServerCallback(SinkServerCallback& serverCallback) {
  serverCallback_ = &serverCallback;
}

void ClientSinkBridge::consume() {
  DCHECK(evb_);
  evb_->runInEventBaseThread(
      [self = copy()]() mutable { self->processServerMessages(); });
}

void ClientSinkBridge::processServerMessages() {
  if (!serverCallback_) {
    return;
  }

  do {
    for (auto messages = serverGetMessages(); !messages.empty();
         messages.pop()) {
      bool terminated = false;
      auto& payload = messages.front();
      if (payload.hasException()) {
        serverCallback_->onSinkError(std::move(payload).exception());
        terminated = true;
      } else if (payload.hasValue()) {
        terminated = !serverCallback_->onSinkNext(std::move(payload).value());
      } else {
        terminated = !serverCallback_->onSinkComplete();
      }

      if (terminated) {
        close();
        return;
      }
    }
  } while (!serverWait(this));
}

bool ClientSinkBridge::hasServerCancelled() {
  return serverCancelSource_.isCancellationRequested();
}

} // namespace detail
} // namespace thrift
} // namespace apache
<|endoftext|>


<commit_before>/*
 * ngtcp2
 *
 * Copyright (c) 2017 ngtcp2 contributors
 *
 * 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 "debug.h"

#include <cassert>
#include <random>
#include <iostream>

#include "util.h"

namespace ngtcp2 {

namespace debug {

namespace {
auto randgen = util::make_mt19937();
} // namespace

namespace {
auto *outfile = stderr;
} // namespace

int handshake_completed(ngtcp2_conn *conn, void *user_data) {
  fprintf(outfile, "QUIC handshake has completed\n");
  return 0;
}

bool packet_lost(double prob) {
  auto p = std::uniform_real_distribution<>(0, 1)(randgen);
  return p < prob;
}

void print_crypto_data(ngtcp2_crypto_level crypto_level, const uint8_t *data,
                       size_t datalen) {
  const char *crypto_level_str;
  switch (crypto_level) {
  case NGTCP2_CRYPTO_LEVEL_INITIAL:
    crypto_level_str = "Initial";
    break;
  case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
    crypto_level_str = "Handshake";
    break;
  case NGTCP2_CRYPTO_LEVEL_APP:
    crypto_level_str = "Application";
    break;
  default:
    assert(0);
  }
  fprintf(outfile, "Ordered CRYPTO data in %s crypto level\n",
          crypto_level_str);
  util::hexdump(outfile, data, datalen);
}

void print_stream_data(int64_t stream_id, const uint8_t *data, size_t datalen) {
  fprintf(outfile, "Ordered STREAM data stream_id=0x%" PRIx64 "\n", stream_id);
  util::hexdump(outfile, data, datalen);
}

void print_initial_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "initial_secret=%s\n", util::format_hex(data, len).c_str());
}

void print_client_in_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "client_in_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_server_in_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "server_in_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_handshake_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "handshake_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_client_hs_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "client_hs_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_server_hs_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "server_hs_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_client_0rtt_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "client_0rtt_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_client_1rtt_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "client_1rtt_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_server_1rtt_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "server_1rtt_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_client_pp_key(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ client_pp_key=%s\n", util::format_hex(data, len).c_str());
}

void print_server_pp_key(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ server_pp_key=%s\n", util::format_hex(data, len).c_str());
}

void print_client_pp_iv(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ client_pp_iv=%s\n", util::format_hex(data, len).c_str());
}

void print_server_pp_iv(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ server_pp_iv=%s\n", util::format_hex(data, len).c_str());
}

void print_client_pp_hp(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ client_pp_hp=%s\n", util::format_hex(data, len).c_str());
}

void print_server_pp_hp(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ server_pp_hp=%s\n", util::format_hex(data, len).c_str());
}

void print_secrets(const uint8_t *secret, size_t secretlen, const uint8_t *key,
                   size_t keylen, const uint8_t *iv, size_t ivlen,
                   const uint8_t *hp, size_t hplen) {
  std::cerr << "+ secret=" << util::format_hex(secret, secretlen) << "\n"
            << "+ key=" << util::format_hex(key, keylen) << "\n"
            << "+ iv=" << util::format_hex(iv, ivlen) << "\n"
            << "+ hp=" << util::format_hex(hp, hplen) << std::endl;
}

void print_secrets(const uint8_t *secret, size_t secretlen, const uint8_t *key,
                   size_t keylen, const uint8_t *iv, size_t ivlen) {
  std::cerr << "+ secret=" << util::format_hex(secret, secretlen) << "\n"
            << "+ key=" << util::format_hex(key, keylen) << "\n"
            << "+ iv=" << util::format_hex(iv, ivlen) << std::endl;
}

void print_hp_mask(const uint8_t *mask, size_t masklen, const uint8_t *sample,
                   size_t samplelen) {
  fprintf(outfile, "mask=%s sample=%s\n",
          util::format_hex(mask, masklen).c_str(),
          util::format_hex(sample, samplelen).c_str());
}

void log_printf(void *user_data, const char *fmt, ...) {
  va_list ap;

  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);

  fprintf(stderr, "\n");
}

void path_validation(const ngtcp2_path *path,
                     ngtcp2_path_validation_result res) {
  auto local_addr = util::straddr(
      reinterpret_cast<sockaddr *>(path->local.addr), path->local.addrlen);
  auto remote_addr = util::straddr(
      reinterpret_cast<sockaddr *>(path->remote.addr), path->remote.addrlen);

  std::cerr << "Path validation against path {local:" << local_addr
            << ", remote:" << remote_addr << "} "
            << (res == NGTCP2_PATH_VALIDATION_RESULT_SUCCESS ? "succeeded"
                                                             : "failed")
            << std::endl;
}

void print_http_begin_request_headers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " request headers started\n",
          stream_id);
}

void print_http_begin_response_headers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " response headers started\n",
          stream_id);
}

namespace {
void print_header(const uint8_t *name, const uint8_t *value, uint8_t flags) {
  fprintf(outfile, "[%s: %s]%s\n", name, value,
          (flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? "(sensitive)" : "");
}
} // namespace

namespace {
void print_header(const nghttp3_rcbuf *name, const nghttp3_rcbuf *value,
                  uint8_t flags) {
  print_header(nghttp3_rcbuf_get_buf(name).base,
               nghttp3_rcbuf_get_buf(value).base, flags);
}
} // namespace

namespace {
void print_header(const nghttp3_nv &nv) {
  print_header(nv.name, nv.value, nv.flags);
}
} // namespace

void print_http_header(int64_t stream_id, const nghttp3_rcbuf *name,
                       const nghttp3_rcbuf *value, uint8_t flags) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " ", stream_id);
  print_header(name, value, flags);
}

void print_http_end_headers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " headers ended\n", stream_id);
}

void print_http_data(int64_t stream_id, const uint8_t *data, size_t datalen) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " body %zu bytes\n", stream_id,
          datalen);
  util::hexdump(outfile, data, datalen);
}

void print_http_begin_trailers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " trailers started\n", stream_id);
}

void print_http_end_trailers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " trailers ended\n", stream_id);
}

void print_http_begin_push_promise(int64_t stream_id, int64_t push_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " push 0x%" PRIx64 " started\n",
          stream_id, push_id);
}

void print_http_push_promise(int64_t stream_id, int64_t push_id,
                             const nghttp3_rcbuf *name,
                             const nghttp3_rcbuf *value, uint8_t flags) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " push 0x%" PRIx64 " ", stream_id,
          push_id);
  print_header(name, value, flags);
}

void print_http_end_push_promise(int64_t stream_id, int64_t push_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " push 0x%" PRIx64 " ended\n",
          stream_id, push_id);
}

void cancel_push(int64_t push_id, int64_t stream_id) {
  fprintf(outfile,
          "http: push 0x%" PRIx64 " (stream 0x%" PRIx64
          ") has been cancelled by remote endpoint\n",
          push_id, stream_id);
}

void push_stream(int64_t push_id, int64_t stream_id) {
  fprintf(outfile,
          "http: push 0x%" PRIx64 " promise fulfilled stream 0x%" PRIx64 "\n",
          push_id, stream_id);
}

void print_http_request_headers(int64_t stream_id, const nghttp3_nv *nva,
                                size_t nvlen) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " submit request headers\n",
          stream_id);
  for (size_t i = 0; i < nvlen; ++i) {
    auto &nv = nva[i];
    print_header(nv);
  }
}

void print_http_response_headers(int64_t stream_id, const nghttp3_nv *nva,
                                 size_t nvlen) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " submit response headers\n",
          stream_id);
  for (size_t i = 0; i < nvlen; ++i) {
    auto &nv = nva[i];
    print_header(nv);
  }
}

void print_http_push_promise(int64_t stream_id, int64_t push_id,
                             const nghttp3_nv *nva, size_t nvlen) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " submit push 0x%" PRIx64 "\n",
          stream_id, push_id);
  for (size_t i = 0; i < nvlen; ++i) {
    auto &nv = nva[i];
    print_header(nv);
  }
}

} // namespace debug

} // namespace ngtcp2
<commit_msg>Fix header printing bug<commit_after>/*
 * ngtcp2
 *
 * Copyright (c) 2017 ngtcp2 contributors
 *
 * 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 "debug.h"

#include <cassert>
#include <random>
#include <iostream>

#include "util.h"

namespace ngtcp2 {

namespace debug {

namespace {
auto randgen = util::make_mt19937();
} // namespace

namespace {
auto *outfile = stderr;
} // namespace

int handshake_completed(ngtcp2_conn *conn, void *user_data) {
  fprintf(outfile, "QUIC handshake has completed\n");
  return 0;
}

bool packet_lost(double prob) {
  auto p = std::uniform_real_distribution<>(0, 1)(randgen);
  return p < prob;
}

void print_crypto_data(ngtcp2_crypto_level crypto_level, const uint8_t *data,
                       size_t datalen) {
  const char *crypto_level_str;
  switch (crypto_level) {
  case NGTCP2_CRYPTO_LEVEL_INITIAL:
    crypto_level_str = "Initial";
    break;
  case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
    crypto_level_str = "Handshake";
    break;
  case NGTCP2_CRYPTO_LEVEL_APP:
    crypto_level_str = "Application";
    break;
  default:
    assert(0);
  }
  fprintf(outfile, "Ordered CRYPTO data in %s crypto level\n",
          crypto_level_str);
  util::hexdump(outfile, data, datalen);
}

void print_stream_data(int64_t stream_id, const uint8_t *data, size_t datalen) {
  fprintf(outfile, "Ordered STREAM data stream_id=0x%" PRIx64 "\n", stream_id);
  util::hexdump(outfile, data, datalen);
}

void print_initial_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "initial_secret=%s\n", util::format_hex(data, len).c_str());
}

void print_client_in_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "client_in_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_server_in_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "server_in_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_handshake_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "handshake_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_client_hs_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "client_hs_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_server_hs_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "server_hs_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_client_0rtt_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "client_0rtt_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_client_1rtt_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "client_1rtt_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_server_1rtt_secret(const uint8_t *data, size_t len) {
  fprintf(outfile, "server_1rtt_secret=%s\n",
          util::format_hex(data, len).c_str());
}

void print_client_pp_key(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ client_pp_key=%s\n", util::format_hex(data, len).c_str());
}

void print_server_pp_key(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ server_pp_key=%s\n", util::format_hex(data, len).c_str());
}

void print_client_pp_iv(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ client_pp_iv=%s\n", util::format_hex(data, len).c_str());
}

void print_server_pp_iv(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ server_pp_iv=%s\n", util::format_hex(data, len).c_str());
}

void print_client_pp_hp(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ client_pp_hp=%s\n", util::format_hex(data, len).c_str());
}

void print_server_pp_hp(const uint8_t *data, size_t len) {
  fprintf(outfile, "+ server_pp_hp=%s\n", util::format_hex(data, len).c_str());
}

void print_secrets(const uint8_t *secret, size_t secretlen, const uint8_t *key,
                   size_t keylen, const uint8_t *iv, size_t ivlen,
                   const uint8_t *hp, size_t hplen) {
  std::cerr << "+ secret=" << util::format_hex(secret, secretlen) << "\n"
            << "+ key=" << util::format_hex(key, keylen) << "\n"
            << "+ iv=" << util::format_hex(iv, ivlen) << "\n"
            << "+ hp=" << util::format_hex(hp, hplen) << std::endl;
}

void print_secrets(const uint8_t *secret, size_t secretlen, const uint8_t *key,
                   size_t keylen, const uint8_t *iv, size_t ivlen) {
  std::cerr << "+ secret=" << util::format_hex(secret, secretlen) << "\n"
            << "+ key=" << util::format_hex(key, keylen) << "\n"
            << "+ iv=" << util::format_hex(iv, ivlen) << std::endl;
}

void print_hp_mask(const uint8_t *mask, size_t masklen, const uint8_t *sample,
                   size_t samplelen) {
  fprintf(outfile, "mask=%s sample=%s\n",
          util::format_hex(mask, masklen).c_str(),
          util::format_hex(sample, samplelen).c_str());
}

void log_printf(void *user_data, const char *fmt, ...) {
  va_list ap;

  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);

  fprintf(stderr, "\n");
}

void path_validation(const ngtcp2_path *path,
                     ngtcp2_path_validation_result res) {
  auto local_addr = util::straddr(
      reinterpret_cast<sockaddr *>(path->local.addr), path->local.addrlen);
  auto remote_addr = util::straddr(
      reinterpret_cast<sockaddr *>(path->remote.addr), path->remote.addrlen);

  std::cerr << "Path validation against path {local:" << local_addr
            << ", remote:" << remote_addr << "} "
            << (res == NGTCP2_PATH_VALIDATION_RESULT_SUCCESS ? "succeeded"
                                                             : "failed")
            << std::endl;
}

void print_http_begin_request_headers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " request headers started\n",
          stream_id);
}

void print_http_begin_response_headers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " response headers started\n",
          stream_id);
}

namespace {
void print_header(const uint8_t *name, size_t namelen, const uint8_t *value,
                  size_t valuelen, uint8_t flags) {
  fprintf(outfile, "[%.*s: %.*s]%s\n", static_cast<int>(namelen), name,
          static_cast<int>(valuelen), value,
          (flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? "(sensitive)" : "");
}
} // namespace

namespace {
void print_header(const nghttp3_rcbuf *name, const nghttp3_rcbuf *value,
                  uint8_t flags) {
  auto namebuf = nghttp3_rcbuf_get_buf(name);
  auto valuebuf = nghttp3_rcbuf_get_buf(value);
  print_header(namebuf.base, namebuf.len, valuebuf.base, valuebuf.len, flags);
}
} // namespace

namespace {
void print_header(const nghttp3_nv &nv) {
  print_header(nv.name, nv.namelen, nv.value, nv.valuelen, nv.flags);
}
} // namespace

void print_http_header(int64_t stream_id, const nghttp3_rcbuf *name,
                       const nghttp3_rcbuf *value, uint8_t flags) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " ", stream_id);
  print_header(name, value, flags);
}

void print_http_end_headers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " headers ended\n", stream_id);
}

void print_http_data(int64_t stream_id, const uint8_t *data, size_t datalen) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " body %zu bytes\n", stream_id,
          datalen);
  util::hexdump(outfile, data, datalen);
}

void print_http_begin_trailers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " trailers started\n", stream_id);
}

void print_http_end_trailers(int64_t stream_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " trailers ended\n", stream_id);
}

void print_http_begin_push_promise(int64_t stream_id, int64_t push_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " push 0x%" PRIx64 " started\n",
          stream_id, push_id);
}

void print_http_push_promise(int64_t stream_id, int64_t push_id,
                             const nghttp3_rcbuf *name,
                             const nghttp3_rcbuf *value, uint8_t flags) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " push 0x%" PRIx64 " ", stream_id,
          push_id);
  print_header(name, value, flags);
}

void print_http_end_push_promise(int64_t stream_id, int64_t push_id) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " push 0x%" PRIx64 " ended\n",
          stream_id, push_id);
}

void cancel_push(int64_t push_id, int64_t stream_id) {
  fprintf(outfile,
          "http: push 0x%" PRIx64 " (stream 0x%" PRIx64
          ") has been cancelled by remote endpoint\n",
          push_id, stream_id);
}

void push_stream(int64_t push_id, int64_t stream_id) {
  fprintf(outfile,
          "http: push 0x%" PRIx64 " promise fulfilled stream 0x%" PRIx64 "\n",
          push_id, stream_id);
}

void print_http_request_headers(int64_t stream_id, const nghttp3_nv *nva,
                                size_t nvlen) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " submit request headers\n",
          stream_id);
  for (size_t i = 0; i < nvlen; ++i) {
    auto &nv = nva[i];
    print_header(nv);
  }
}

void print_http_response_headers(int64_t stream_id, const nghttp3_nv *nva,
                                 size_t nvlen) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " submit response headers\n",
          stream_id);
  for (size_t i = 0; i < nvlen; ++i) {
    auto &nv = nva[i];
    print_header(nv);
  }
}

void print_http_push_promise(int64_t stream_id, int64_t push_id,
                             const nghttp3_nv *nva, size_t nvlen) {
  fprintf(outfile, "http: stream 0x%" PRIx64 " submit push 0x%" PRIx64 "\n",
          stream_id, push_id);
  for (size_t i = 0; i < nvlen; ++i) {
    auto &nv = nva[i];
    print_header(nv);
  }
}

} // namespace debug

} // namespace ngtcp2
<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "JpegCompressor.h"

#include "logging/Logger.h"

#include <jpeglib.h>

#include <cerrno>
#include <cstring>
#include <memory>
#include <setjmp.h>

namespace medialibrary
{

const char* JpegCompressor::extension() const
{
    return "jpg";
}

const char* JpegCompressor::fourCC() const
{
    return "RV24";
}

uint32_t JpegCompressor::bpp() const
{
    return 3;
}

struct jpegError : public jpeg_error_mgr
{
    jmp_buf buff;
    char message[JMSG_LENGTH_MAX];

    static void jpegErrorHandler( j_common_ptr common )
    {
        auto error = reinterpret_cast<jpegError*>( common->err );
        (*error->format_message)( common, error->message );
        longjmp( error->buff, 1 );
    }
};

bool JpegCompressor::compress( const uint8_t* buffer, const std::string& outputFile,
                              uint32_t inputWidth, uint32_t,
                              uint32_t outputWidth, uint32_t outputHeight,
                              uint32_t hOffset, uint32_t vOffset )
{
    const auto stride = inputWidth * bpp();

    //FIXME: Abstract this away, though libjpeg requires a FILE*...
    auto fOut = std::unique_ptr<FILE, int(*)(FILE*)>( fopen( outputFile.c_str(), "wb" ), &fclose );
    if ( fOut == nullptr )
    {
        LOG_ERROR( "Failed to open thumbnail file ", outputFile, '(', strerror( errno ), ')' );
        return false;
    }

    jpeg_compress_struct compInfo;
    JSAMPROW row_pointer[1];

    //libjpeg's default error handling is to call exit(), which would
    //be slightly problematic...
    jpegError err;
    compInfo.err = jpeg_std_error(&err);
    err.error_exit = jpegError::jpegErrorHandler;

    if ( setjmp( err.buff ) )
    {
        LOG_ERROR("JPEG failure: ", err.message);
        jpeg_destroy_compress(&compInfo);
        return false;
    }

    jpeg_create_compress(&compInfo);
    jpeg_stdio_dest(&compInfo, fOut.get());

    compInfo.image_width = outputWidth;
    compInfo.image_height = outputHeight;
    compInfo.input_components = bpp();
    compInfo.in_color_space = JCS_RGB;
    jpeg_set_defaults( &compInfo );
    jpeg_set_quality( &compInfo, 85, TRUE );

    jpeg_start_compress( &compInfo, TRUE );

    while ( compInfo.next_scanline < outputHeight )
    {
        row_pointer[0] = const_cast<uint8_t*>( &buffer[(compInfo.next_scanline + vOffset ) * stride + hOffset * bpp()] );
        jpeg_write_scanlines(&compInfo, row_pointer, 1);
    }
    jpeg_finish_compress(&compInfo);
    jpeg_destroy_compress(&compInfo);
    return true;
}

}
<commit_msg>JpegCompressor: Reduce quality to 80<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "JpegCompressor.h"

#include "logging/Logger.h"

#include <jpeglib.h>

#include <cerrno>
#include <cstring>
#include <memory>
#include <setjmp.h>

namespace medialibrary
{

const char* JpegCompressor::extension() const
{
    return "jpg";
}

const char* JpegCompressor::fourCC() const
{
    return "RV24";
}

uint32_t JpegCompressor::bpp() const
{
    return 3;
}

struct jpegError : public jpeg_error_mgr
{
    jmp_buf buff;
    char message[JMSG_LENGTH_MAX];

    static void jpegErrorHandler( j_common_ptr common )
    {
        auto error = reinterpret_cast<jpegError*>( common->err );
        (*error->format_message)( common, error->message );
        longjmp( error->buff, 1 );
    }
};

bool JpegCompressor::compress( const uint8_t* buffer, const std::string& outputFile,
                              uint32_t inputWidth, uint32_t,
                              uint32_t outputWidth, uint32_t outputHeight,
                              uint32_t hOffset, uint32_t vOffset )
{
    const auto stride = inputWidth * bpp();

    //FIXME: Abstract this away, though libjpeg requires a FILE*...
    auto fOut = std::unique_ptr<FILE, int(*)(FILE*)>( fopen( outputFile.c_str(), "wb" ), &fclose );
    if ( fOut == nullptr )
    {
        LOG_ERROR( "Failed to open thumbnail file ", outputFile, '(', strerror( errno ), ')' );
        return false;
    }

    jpeg_compress_struct compInfo;
    JSAMPROW row_pointer[1];

    //libjpeg's default error handling is to call exit(), which would
    //be slightly problematic...
    jpegError err;
    compInfo.err = jpeg_std_error(&err);
    err.error_exit = jpegError::jpegErrorHandler;

    if ( setjmp( err.buff ) )
    {
        LOG_ERROR("JPEG failure: ", err.message);
        jpeg_destroy_compress(&compInfo);
        return false;
    }

    jpeg_create_compress(&compInfo);
    jpeg_stdio_dest(&compInfo, fOut.get());

    compInfo.image_width = outputWidth;
    compInfo.image_height = outputHeight;
    compInfo.input_components = bpp();
    compInfo.in_color_space = JCS_RGB;
    jpeg_set_defaults( &compInfo );
    jpeg_set_quality( &compInfo, 80, TRUE );

    jpeg_start_compress( &compInfo, TRUE );

    while ( compInfo.next_scanline < outputHeight )
    {
        row_pointer[0] = const_cast<uint8_t*>( &buffer[(compInfo.next_scanline + vOffset ) * stride + hOffset * bpp()] );
        jpeg_write_scanlines(&compInfo, row_pointer, 1);
    }
    jpeg_finish_compress(&compInfo);
    jpeg_destroy_compress(&compInfo);
    return true;
}

}
<|endoftext|>


<commit_before>//
//  test_observer.cpp
//  hardware-control
//
//  Created by Julian Becker on 03.09.15.
//  Copyright (c) 2015 Julian Becker. All rights reserved.
//
#include "../catch.h"
#include <stdio.h>
#include <memory>
#include <list>
#include <map>
#include <functional>

template <typename T> struct
inotifyable {
    virtual void handle(T&&) = 0;
};

template <typename _Kty> struct
with_destructor {
    std::map<_Kty,std::function<void()>> functors;
public:
    template <typename Callable>
    void add_raii(_Kty key, Callable fun) {
        functors[std::move(key)] = std::move(fun);
    }

    void remove_raii(_Kty key) {
        functors.erase(std::move(key));
    }

    virtual ~with_destructor() {
        for(auto& f : functors)
            f.second();
    }
};

template <typename T> struct
ilistener;

template <typename T> struct
iobservable;

template <typename T> struct
ilistener : inotifyable<T>, with_destructor<iobservable<T>*> {
};

template <typename T> struct
iobservable {
};

template <typename T>
class listener;

template <typename T>
class observable : public iobservable<T> {
    T value;
    std::set<ilistener<T>*> listeners;

public:
    observable(T value)
    : value(value)
    {
    }

    observable(const observable& other)
    : value(other.value), listeners(other.listeners)
    {
        for(auto& l : listeners)
            l->add_raii(this,[this,&l]{ unregisterListener(*l); });
    }

    observable(observable&& other)
    : value(std::move(other.value)), listeners(std::move(other.listeners))
    {
        for(auto& l : listeners) {
            l->remove_raii(&other);
            l->add_raii(this,[this,&l]{ unregisterListener(*l); });
        }
    }

    observable& operator = (const observable& other) {
        if(this != &other) {
            observable copy(other);
            *this = std::move(copy);
        }
        return *this;
    }

    observable& operator = (observable&& other) {
        listeners = std::move(other.listeners);
        value = std::move(value);
        for(auto& l : listeners) {
            l->remove_raii(&other);
            l->add_raii(this,[this,&l]{ unregisterListener(*l); });
        }
        return *this;
    }

    ~observable() {
        for(auto& l : listeners)
            l->remove_raii(this);
    }

    void registerListener(ilistener<T>& l) {
        listeners.insert(&l);
        l.add_raii(this,[this,&l]{ unregisterListener(l); });
    }

    void unregisterListener(ilistener<T>& l) {
        listeners.erase(&l);
    }

    observable& operator=(const T& val) {
        value = val;
        for(auto& lstnr : listeners)
            lstnr->handle(T(val));
        return *this;
    }

    operator const T& () const {
        return value;
    }

};

template <typename T>
class listener final : public ilistener<T> {
    std::function<void(T&&)> behavior;

public:
    template <typename LAMBDA>
    listener(LAMBDA lam)
      : behavior(std::forward<LAMBDA>(lam))
    {
    }

    void handle(T&& arg) override {
        behavior(std::move(arg));
    }

    ~listener() {
    }
};

TEST_CASE( "Create an observable", "[observable]" ) {
    WHEN("constructing an observable<int>") {
        THEN("no exception is thrown") {
            REQUIRE_NOTHROW(observable<int>(0));
        }
    }

    WHEN("consturcting an observable<T> with T throwing in its constructor") {
        struct t_throws_in_constructor {
            t_throws_in_constructor() { throw nullptr; }
        };
        THEN("the constructor of observable<T> will also throw") {
            REQUIRE_THROWS_AS(t_throws_in_constructor(), std::nullptr_t);
        }
    }
}

TEST_CASE( "Create a listener", "[listener]" ) {
    WHEN("constructing an listener<int>") {
        THEN("no exception is thrown") {
            REQUIRE_NOTHROW(listener<int>([](int&&){}));
        }
    }
}

TEST_CASE("Register listener at observable", "[observable][listener]") {
    GIVEN("a listener and an observable") {
        observable<int> obs(0);
        listener<int>   lst([](int&&){});
        WHEN("registering the listener at the observable") {
            THEN("no exception is thrown") {
                REQUIRE_NOTHROW(obs.registerListener(lst));
            }
        }
    }
}

TEST_CASE("Observable is copyable", "[observable][listener]") {
    observable<int> obs(0);
    observable<int> obsCopy(obs);
}

TEST_CASE("Observable copy behaves exactly like the original", "[observable][listener]") {
    GIVEN("an observable and a listener registered at the observable") {
        observable<int> obs(0);
        bool triggered = false;
        listener<int> lst([&triggered](int&&){ triggered = true; });
        obs.registerListener(lst);

        THEN("creating a copy of the observabe and modifying the value of the copy will trigger all listeners that have been registered at the initial observable") {
            observable<int> obsCopy(obs);
            obsCopy = 42;
            REQUIRE(triggered);
        }
    }
}

TEST_CASE("Observable copy does not result in segfault", "[observable][listener]") {
    GIVEN("an observable and a listener registered at the observable") {
        auto obs = std::make_shared<observable<int>>(0);
        bool triggered = false;
        auto lst = std::make_shared<listener<int>>([&triggered](int&&){ triggered = true; });
        obs->registerListener(*lst);

        THEN("creating a copy of the observabe and destroying the original and the listener will not result in a segfault (and will not trigger the listener)") {
            observable<int> obsCopy(*obs);
            obs = nullptr;
            lst = nullptr;
            REQUIRE(!triggered);
        }

        AND_THEN("modifying the copied observable after the original has been destroyed still triggers the listener") {
            observable<int> obsCopy(*obs);
            obs = nullptr;
            obsCopy = 42;
            REQUIRE(triggered);
        }
    }
}

TEST_CASE("Assign value to observable","[observable]") {
    GIVEN("We may assign an value to an observable") {
        observable<int> obs(0);
        obs = 1;
    }

    WHEN("an observable is constructed") {
        observable<int> obs(0x1337);
        THEN("the value it holds is the value given the constructor") {
            REQUIRE(0x1337 == obs);
        }
    }

    WHEN("a value is assigned to an observable") {
        observable<int> obs(0);
        obs = 42;
        THEN("the same value can be retrieved back from the observable") {
            REQUIRE(42 == obs);
        }
    }
}

TEST_CASE("Assigning value triggers observer", "[observable][listener]") {
    GIVEN("an observable<int> and a listener<int>") {
        observable<int> obs(0);
        bool triggered = false;
        listener<int> lstnr([&triggered](int&&){ triggered = true; });
        WHEN("listener registers at observable and a value is put into the observable") {
            obs.registerListener(lstnr);
            obs = 42;
            THEN("the listener is triggered") {
                REQUIRE(triggered);
            }
        }
    }
}

TEST_CASE("Assigning value triggers all listeners", "[observable][listener]") {
    GIVEN("an observable<int> and a several listener<int>") {
        observable<int> obs(0);
        bool triggered1 = false, triggered2 = false, triggered3 = false;
        listener<int> lstnr1([&triggered1](int&&){ triggered1 = true; });
        listener<int> lstnr2([&triggered2](int&&){ triggered2 = true; });
        listener<int> lstnr3([&triggered3](int&&){ triggered3 = true; });
        WHEN("several listeners registers at observable and a value is put into the observable") {
            obs.registerListener(lstnr1);
            obs.registerListener(lstnr2);
            obs.registerListener(lstnr3);
            obs = 42;
            THEN("all registered listeners are triggered") {
                REQUIRE(triggered1);
                REQUIRE(triggered2);
                REQUIRE(triggered3);
            }
        }
    }
}

TEST_CASE("Unregister listener", "[observable][listener]") {
    GIVEN("an observable<int> and a listener<int>") {
        observable<int> obs(0);
        bool triggered = false;
        listener<int> lstnr([&triggered](int&&){ triggered = true; });
        WHEN("listener registers and unregisteres at the observable and a value is put into the observable") {
            obs.registerListener(lstnr);
            obs.unregisterListener(lstnr);
            obs = 42;
            THEN("the listener is not triggered") {
                REQUIRE(!triggered);
            }
        }
    }
}

TEST_CASE("destroying listener before observable must be safe", "[observable][listener]") {
    GIVEN("an observable<int> and a listener<int>") {
        auto obs = std::make_shared<observable<int>>(0);
        bool triggered = false;
        auto lstnr = std::make_shared<listener<int>>([&triggered](int&&){ triggered = true; });

        WHEN("the listener is destroyed and the observable triggered") {
            obs->registerListener(*lstnr);
            lstnr=0;
            *obs = 37;
            THEN("the program does not crash and the registered listener is still not triggered") {
                REQUIRE(!triggered);
            }
        }
    }
}

TEST_CASE("destroying observable before listener must be safe", "[observable][listener]") {
    GIVEN("an observable<int> and a listener<int>") {
        auto obs = std::make_shared<observable<int>>(0);
        auto lstnr = std::make_shared<listener<int>>([](int&&){});

        WHEN("the observable is destroyed the registered listener can be safely destructed") {
            obs->registerListener(*lstnr);
            obs = nullptr;

            int* x;

            THEN("the program does not crash and the registered listener is still not triggered") {
                lstnr = nullptr;
            }
        }
    }
}

TEST_CASE("moving an observable into another observable keeps the listeners intact") {
    GIVEN("an observable and a listener") {
        observable<int> obs(0);
        bool triggered = false;
        auto lstnr = std::make_shared<listener<int>>([&triggered](int&&){ triggered = true; });
        WHEN("the listener is registered it the observable") {
            obs.registerListener(*lstnr);
            AND_WHEN("the observable is move-constructed into a different observable") {
                observable<int> obs2(std::move(obs));
                THEN("modifying the moved-to observable will trigger the listener") {
                    obs2 = 42;
                    REQUIRE(triggered);
                }
            }
            AND_WHEN("the observable is moved into a different observable") {
                observable<int> obs2(0);
                obs2 = std::move(obs);
                THEN("modifying the moved-to observable will trigger the listener") {
                    obs2 = 42;
                    REQUIRE(triggered);
                }
            }
        }
    }
}<commit_msg>reworked test case to use a mock for ilistener<int> instead of the concrete class listener<int>.<commit_after>//
//  test_observer.cpp
//  hardware-control
//
//  Created by Julian Becker on 03.09.15.
//  Copyright (c) 2015 Julian Becker. All rights reserved.
//
#include "../catch.h"
#include <stdio.h>
#include <memory>
#include <list>
#include <map>
#include <functional>

template <typename T> struct
inotifyable {
    virtual void handle(T&&) = 0;
};

template <typename _Kty> struct
with_destructor {
    std::map<_Kty,std::function<void()>> functors;
public:
    template <typename Callable>
    void add_raii(_Kty key, Callable fun) {
        functors[std::move(key)] = std::move(fun);
    }

    void remove_raii(_Kty key) {
        functors.erase(std::move(key));
    }

    virtual ~with_destructor() {
        for(auto& f : functors)
            f.second();
    }
};

template <typename T> struct
ilistener;

template <typename T> struct
iobservable {
    virtual void registerListener(ilistener<T>& l) = 0;

    virtual void unregisterListener(ilistener<T>& l) = 0;
};

template <typename T> struct
ilistener : inotifyable<T>, with_destructor<iobservable<T>*> {
};

template <typename T>
class observable : public iobservable<T> {
    T value;
    std::set<ilistener<T>*> listeners;

public:
    observable(T value)
    : value(value)
    {
    }

    observable(const observable& other)
    : value(other.value), listeners(other.listeners)
    {
        for(auto& l : listeners)
            l->add_raii(this,[this,&l]{ unregisterListener(*l); });
    }

    observable(observable&& other)
    : value(std::move(other.value)), listeners(std::move(other.listeners))
    {
        for(auto& l : listeners) {
            l->remove_raii(&other);
            l->add_raii(this,[this,&l]{ unregisterListener(*l); });
        }
    }

    observable& operator = (const observable& other) {
        if(this != &other) {
            observable copy(other);
            *this = std::move(copy);
        }
        return *this;
    }

    observable& operator = (observable&& other) {
        listeners = std::move(other.listeners);
        value = std::move(value);
        for(auto& l : listeners) {
            l->remove_raii(&other);
            l->add_raii(this,[this,&l]{ unregisterListener(*l); });
        }
        return *this;
    }

    ~observable() {
        for(auto& l : listeners)
            l->remove_raii(this);
    }

    void registerListener(ilistener<T>& l) override {
        listeners.insert(&l);
        l.add_raii(this,[this,&l]{ unregisterListener(l); });
    }

    void unregisterListener(ilistener<T>& l) override {
        listeners.erase(&l);
    }

    observable& operator=(const T& val) {
        value = val;
        for(auto& lstnr : listeners)
            lstnr->handle(T(val));
        return *this;
    }

    operator const T& () const {
        return value;
    }

};

template <typename T>
class listener final : public ilistener<T> {
    std::function<void(T&&)> behavior;

public:
    template <typename LAMBDA>
    listener(LAMBDA lam)
      : behavior(std::forward<LAMBDA>(lam))
    {
    }

    void handle(T&& arg) override {
        behavior(std::move(arg));
    }

    ~listener() {
    }
};

TEST_CASE( "Create an observable", "[observable]" ) {
    WHEN("constructing an observable<int>") {
        THEN("no exception is thrown") {
            REQUIRE_NOTHROW(observable<int>(0));
        }
    }

    WHEN("consturcting an observable<T> with T throwing in its constructor") {
        struct t_throws_in_constructor {
            t_throws_in_constructor() { throw nullptr; }
        };
        THEN("the constructor of observable<T> will also throw") {
            REQUIRE_THROWS_AS(t_throws_in_constructor(), std::nullptr_t);
        }
    }
}

TEST_CASE( "Create a listener", "[listener]" ) {
    WHEN("constructing an listener<int>") {
        THEN("no exception is thrown") {
            REQUIRE_NOTHROW(listener<int>([](int&&){}));
        }
    }
}

TEST_CASE("Register listener at observable", "[observable][listener]") {
    GIVEN("a listener and an observable") {
        observable<int> obs(0);
        listener<int>   lst([](int&&){});
        WHEN("registering the listener at the observable") {
            THEN("no exception is thrown") {
                REQUIRE_NOTHROW(obs.registerListener(lst));
            }
        }
    }
}

TEST_CASE("Observable is copyable", "[observable]") {
    observable<int> obs(0);
    observable<int> obsCopy(obs);
}

TEST_CASE("Observable copy behaves exactly like the original", "[observable]") {
    GIVEN("an observable and a listener registered at the observable") {
        struct test_listener final : ilistener<int> {
            int value = 0;
            bool triggered = false;

            void handle(int&& i) {
                value = i;
                triggered = true;
            }
        } lst;

        observable<int> obs(0);
        obs.registerListener(lst);

        THEN("creating a copy of the observabe and modifying the value of the copy will trigger all listeners that have been registered at the initial observable") {
            observable<int> obsCopy(obs);
            obsCopy = 42;
            REQUIRE(lst.triggered);
        }
    }
}

TEST_CASE("Observable copy does not result in segfault", "[observable][listener]") {
    GIVEN("an observable and a listener registered at the observable") {
        auto obs = std::make_shared<observable<int>>(0);
        bool triggered = false;
        auto lst = std::make_shared<listener<int>>([&triggered](int&&){ triggered = true; });
        obs->registerListener(*lst);

        THEN("creating a copy of the observabe and destroying the original and the listener will not result in a segfault (and will not trigger the listener)") {
            observable<int> obsCopy(*obs);
            obs = nullptr;
            lst = nullptr;
            REQUIRE(!triggered);
        }

        AND_THEN("modifying the copied observable after the original has been destroyed still triggers the listener") {
            observable<int> obsCopy(*obs);
            obs = nullptr;
            obsCopy = 42;
            REQUIRE(triggered);
        }
    }
}

TEST_CASE("Assign value to observable","[observable]") {
    GIVEN("We may assign an value to an observable") {
        observable<int> obs(0);
        obs = 1;
    }

    WHEN("an observable is constructed") {
        observable<int> obs(0x1337);
        THEN("the value it holds is the value given the constructor") {
            REQUIRE(0x1337 == obs);
        }
    }

    WHEN("a value is assigned to an observable") {
        observable<int> obs(0);
        obs = 42;
        THEN("the same value can be retrieved back from the observable") {
            REQUIRE(42 == obs);
        }
    }
}

TEST_CASE("Assigning value triggers observer", "[observable][listener]") {
    GIVEN("an observable<int> and a listener<int>") {
        observable<int> obs(0);
        bool triggered = false;
        listener<int> lstnr([&triggered](int&&){ triggered = true; });
        WHEN("listener registers at observable and a value is put into the observable") {
            obs.registerListener(lstnr);
            obs = 42;
            THEN("the listener is triggered") {
                REQUIRE(triggered);
            }
        }
    }
}

TEST_CASE("Assigning value triggers all listeners", "[observable][listener]") {
    GIVEN("an observable<int> and a several listener<int>") {
        observable<int> obs(0);
        bool triggered1 = false, triggered2 = false, triggered3 = false;
        listener<int> lstnr1([&triggered1](int&&){ triggered1 = true; });
        listener<int> lstnr2([&triggered2](int&&){ triggered2 = true; });
        listener<int> lstnr3([&triggered3](int&&){ triggered3 = true; });
        WHEN("several listeners registers at observable and a value is put into the observable") {
            obs.registerListener(lstnr1);
            obs.registerListener(lstnr2);
            obs.registerListener(lstnr3);
            obs = 42;
            THEN("all registered listeners are triggered") {
                REQUIRE(triggered1);
                REQUIRE(triggered2);
                REQUIRE(triggered3);
            }
        }
    }
}

TEST_CASE("Unregister listener", "[observable][listener]") {
    GIVEN("an observable<int> and a listener<int>") {
        observable<int> obs(0);
        bool triggered = false;
        listener<int> lstnr([&triggered](int&&){ triggered = true; });
        WHEN("listener registers and unregisteres at the observable and a value is put into the observable") {
            obs.registerListener(lstnr);
            obs.unregisterListener(lstnr);
            obs = 42;
            THEN("the listener is not triggered") {
                REQUIRE(!triggered);
            }
        }
    }
}

TEST_CASE("destroying listener before observable must be safe", "[observable][listener]") {
    GIVEN("an observable<int> and a listener<int>") {
        auto obs = std::make_shared<observable<int>>(0);
        bool triggered = false;
        auto lstnr = std::make_shared<listener<int>>([&triggered](int&&){ triggered = true; });

        WHEN("the listener is destroyed and the observable triggered") {
            obs->registerListener(*lstnr);
            lstnr=0;
            *obs = 37;
            THEN("the program does not crash and the registered listener is still not triggered") {
                REQUIRE(!triggered);
            }
        }
    }
}

TEST_CASE("destroying observable before listener must be safe", "[observable][listener]") {
    GIVEN("an observable<int> and a listener<int>") {
        auto obs = std::make_shared<observable<int>>(0);
        auto lstnr = std::make_shared<listener<int>>([](int&&){});

        WHEN("the observable is destroyed the registered listener can be safely destructed") {
            obs->registerListener(*lstnr);
            obs = nullptr;

            int* x;

            THEN("the program does not crash and the registered listener is still not triggered") {
                lstnr = nullptr;
            }
        }
    }
}

TEST_CASE("moving an observable into another observable keeps the listeners intact") {
    GIVEN("an observable and a listener") {
        observable<int> obs(0);
        bool triggered = false;
        auto lstnr = std::make_shared<listener<int>>([&triggered](int&&){ triggered = true; });
        WHEN("the listener is registered it the observable") {
            obs.registerListener(*lstnr);
            AND_WHEN("the observable is move-constructed into a different observable") {
                observable<int> obs2(std::move(obs));
                THEN("modifying the moved-to observable will trigger the listener") {
                    obs2 = 42;
                    REQUIRE(triggered);
                }
            }
            AND_WHEN("the observable is moved into a different observable") {
                observable<int> obs2(0);
                obs2 = std::move(obs);
                THEN("modifying the moved-to observable will trigger the listener") {
                    obs2 = 42;
                    REQUIRE(triggered);
                }
            }
        }
    }
}<|endoftext|>


<commit_before>#include"mfem.hpp"

#include<memory>
#include<iostream>
#include<fstream>

namespace mfem {

class LinDiffQFunc
{
public:
    LinDiffQFunc(mfem::Coefficient& dd, mfem::Coefficient& ll, double gg_,
                 double pp0_, double pp1_):diff(dd),load(ll),gg(gg_),pp0(pp0_),pp1(pp1_)
    {

    }

    double QEnergy(ElementTransformation &T,
                  const IntegrationPoint &ip,
                  mfem::Vector& param, mfem::Vector& uu)
    {
        double dd=diff.Eval(T,ip);
        double ll=load.Eval(T,ip);

        double rho0=param[0];
        double rho1=param[1];
        double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);

        double rez = 0.5*(uu[0]*uu[0]+uu[1]*uu[1]+uu[2]*uu[2])*fd
                        + 0.5*gg*uu[3]*uu[3] -uu[3]*ll;

        return rez;
    }

    void QResidual(ElementTransformation &T,
                   const IntegrationPoint &ip,
                   mfem::Vector& param, mfem::Vector& uu, mfem::Vector& rr)
    {
        rr.SetSize(4);
        double dd=diff.Eval(T,ip);
        double ll=load.Eval(T,ip);

        double rho0=param[0];
        double rho1=param[1];

        double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);

        rr[0]=uu[0]*fd;
        rr[1]=uu[1]*fd;
        rr[2]=uu[2]*fd;
        rr[3]=gg*uu[3]-ll;
    }

    void AQResidual(ElementTransformation &T,
                    const IntegrationPoint &ip,
                    mfem::Vector& param,
                    mfem::Vector& uu, mfem::Vector& aa, mfem::Vector& rr)
    {
         rr.SetSize(2);
         double dd=diff.Eval(T,ip);
         double ll=load.Eval(T,ip);

         double rho0=param[0];
         double rho1=param[1];

         double fd0=dd*pp0*std::pow(rho0,pp0-1.0)*std::pow(rho1,pp1);
         double fd1=dd*std::pow(rho0,pp0)*pp1*std::pow(rho1,pp1-1.0);

         rr[0] = (aa[0]*uu[0]+aa[1]*uu[1]+aa[2]*uu[2])*fd0;
         rr[1] = (aa[0]*uu[0]+aa[1]*uu[1]+aa[2]*uu[2])*fd1;

    }

    void QGradResidual(ElementTransformation &T,
                       const IntegrationPoint &ip,
                       mfem::Vector& param, mfem::Vector& uu, mfem::DenseMatrix& hh)
    {
        hh.SetSize(4);
        double dd=diff.Eval(T,ip);
        //double ll=load.Eval(T,ip);

        double rho0=param[0];
        double rho1=param[1];

        double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);
        hh=0.0;

        hh(0,0)=fd;
        hh(1,1)=fd;
        hh(2,2)=fd;
        hh(3,3)=gg;
    }

private:
    mfem::Coefficient& diff;
    mfem::Coefficient& load;
    double gg;
    double pp0;
    double pp1;
};

class PrmBlockLSFEMDiffusion: public PrmBlockNonlinearFormIntegrator
{
public:
    PrmBlockLSFEMDiffusion(LinDiffQFunc& qfun_)
    {
        qfunc=&qfun_;
    }

    /// Compute the local energy
    virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
                                    const Array<const FiniteElement *>&pel,
                                    ElementTransformation &Tr,
                                    const Array<const Vector *>&elfun,
                                    const Array<const Vector *>&pelfun)
    {
        int dof_u0 = el[0]->GetDof();
        int dof_r0 = pel[0]->GetDof();
        int dof_r1 = pel[1]->GetDof();

        int dim = el[0]->GetDim();
        int spaceDim = Tr.GetSpaceDim();
        if (dim != spaceDim)
        {
            mfem::mfem_error(" PrmBlockLSFEMDiffusion::GetElementEnergy"
                             " is not defined on manifold meshes");
        }

        //shape functions
        Vector shu0(dof_u0);
        Vector shr0(dof_r0);
        Vector shr1(dof_r1);
        DenseMatrix dsu0(dof_u0,dim);
        DenseMatrix B(dof_u0, 4);
        B=0.0;

        double w;

        Vector param(2); param=0.0;
        Vector uu(4); uu=0.0;

        double energy =0.0;

        const IntegrationRule *ir = nullptr;
        if(ir==nullptr){
            int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
                                +pel[0]->GetOrder()+pel[1]->GetOrder();
            ir=&IntRules.Get(Tr.GetGeometryType(),order);
        }

        for (int i = 0; i < ir->GetNPoints(); i++)
        {
            const IntegrationPoint &ip = ir->IntPoint(i);
            Tr.SetIntPoint(&ip);
            w=Tr.Weight();
            w = ip.weight * w;

            el[0]->CalcPhysDShape(Tr,dsu0);
            el[0]->CalcPhysShape(Tr,shu0);
            pel[0]->CalcPhysShape(Tr,shr0);
            pel[1]->CalcPhysShape(Tr,shr1);

            param[0]=shr0*(*pelfun[0]);
            param[1]=shr1*(*pelfun[1]);

            //set the matrix B
            for(int jj=0;jj<dim;jj++)
            {
                B.SetCol(jj,dsu0.GetColumn(jj));
            }
            B.SetCol(3,shu0);
            B.MultTranspose(*elfun[0],uu);
            energy=energy+w * qfunc->QEnergy(Tr,ip,param,uu);
        }
        return energy;
    }

    /// Perform the local action of the BlockNonlinearFormIntegrator
    virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
                                       const Array<const FiniteElement *>&pel,
                                       ElementTransformation &Tr,
                                       const Array<const Vector *> &elfun,
                                       const Array<const Vector *>&pelfun,
                                       const Array<Vector *> &elvec)
    {
        int dof_u0 = el[0]->GetDof();
        int dof_r0 = pel[0]->GetDof();
        int dof_r1 = pel[1]->GetDof();

        int dim = el[0]->GetDim();

        elvec[0]->SetSize(dof_u0);
        *elvec[0]=0.0;
        int spaceDim = Tr.GetSpaceDim();
        if (dim != spaceDim)
        {
            mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
                             " is not defined on manifold meshes");
        }

        //shape functions
        Vector shu0(dof_u0);
        Vector shr0(dof_r0);
        Vector shr1(dof_r1);
        DenseMatrix dsu0(dof_u0,dim);
        DenseMatrix B(dof_u0, 4);
        B=0.0;

        double w;

        Vector param(2); param=0.0;
        Vector uu(4); uu=0.0;
        Vector rr;
        Vector lvec; lvec.SetSize(dof_u0);

        const IntegrationRule *ir = nullptr;
        if(ir==nullptr){
            int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
                                +pel[0]->GetOrder()+pel[1]->GetOrder();
            ir=&IntRules.Get(Tr.GetGeometryType(),order);
        }

        for (int i = 0; i < ir->GetNPoints(); i++)
        {
            const IntegrationPoint &ip = ir->IntPoint(i);
            Tr.SetIntPoint(&ip);
            w=Tr.Weight();
            w = ip.weight * w;

            el[0]->CalcPhysDShape(Tr,dsu0);
            el[0]->CalcPhysShape(Tr,shu0);
            pel[0]->CalcPhysShape(Tr,shr0);
            pel[1]->CalcPhysShape(Tr,shr1);

            param[0]=shr0*(*pelfun[0]);
            param[1]=shr1*(*pelfun[1]);

            //set the matrix B
            for(int jj=0;jj<dim;jj++)
            {
                B.SetCol(jj,dsu0.GetColumn(jj));
            }
            B.SetCol(3,shu0);
            B.MultTranspose(*elfun[0],uu);
            qfunc->QResidual(Tr,ip,param, uu, rr);

            B.Mult(rr,lvec);
            elvec[0]->Add(w,lvec);
        }

    }

    virtual void AssembleFaceVector(const Array<const FiniteElement *> &el1,
                                    const Array<const FiniteElement *> &el2,
                                    const Array<const FiniteElement *> &pel1,
                                    const Array<const FiniteElement *> &pel2,
                                    FaceElementTransformations &Tr,
                                    const Array<const Vector *> &elfun,
                                    const Array<const Vector *>&pelfun,
                                    const Array<Vector *> &elvect)
    {

    }

    /// Assemble the local gradient matrix
    virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
                                     const Array<const FiniteElement *>&pel,
                                     ElementTransformation &Tr,
                                     const Array<const Vector *> &elfun,
                                     const Array<const Vector *>&pelfun,
                                     const Array2D<DenseMatrix *> &elmats)
    {
        int dof_u0 = el[0]->GetDof();
        int dof_r0 = pel[0]->GetDof();
        int dof_r1 = pel[1]->GetDof();

        int dim = el[0]->GetDim();

        //elmats[0]->Size(dof_u0, dof_u0);
        //*elmats[0]=0.0;

        DenseMatrix* K=elmats(0,0);
        K->SetSize(dof_u0,dof_u0);
        (*K)=0.0;

        int spaceDim = Tr.GetSpaceDim();
        if (dim != spaceDim)
        {
            mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
                             " is not defined on manifold meshes");
        }

        //shape functions
        Vector shu0(dof_u0);
        Vector shr0(dof_r0);
        Vector shr1(dof_r1);
        DenseMatrix dsu0(dof_u0,dim);
        DenseMatrix B(dof_u0, 4);
        DenseMatrix A(dof_u0, 4);
        B=0.0;

        double w;

        Vector param(2); param=0.0;
        Vector uu(4); uu=0.0;
        DenseMatrix hh;
        Vector lvec; lvec.SetSize(dof_u0);

        const IntegrationRule *ir = nullptr;
        if(ir==nullptr){
            int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
                                +pel[0]->GetOrder()+pel[1]->GetOrder();
            ir=&IntRules.Get(Tr.GetGeometryType(),order);
        }

        for (int i = 0; i < ir->GetNPoints(); i++)
        {
            const IntegrationPoint &ip = ir->IntPoint(i);
            Tr.SetIntPoint(&ip);
            w = Tr.Weight();
            w = ip.weight * w;

            el[0]->CalcPhysDShape(Tr,dsu0);
            el[0]->CalcPhysShape(Tr,shu0);
            pel[0]->CalcPhysShape(Tr,shr0);
            pel[1]->CalcPhysShape(Tr,shr1);

            param[0]=shr0*(*pelfun[0]);
            param[1]=shr1*(*pelfun[1]);

            //set the matrix B
            for(int jj=0;jj<dim;jj++)
            {
                B.SetCol(jj,dsu0.GetColumn(jj));
            }
            B.SetCol(3,shu0);
            B.MultTranspose(*elfun[0],uu);
            qfunc->QGradResidual(Tr,ip,param,uu,hh);
            Mult(B,hh,A);
            AddMult_a_ABt(w,A,B,*K);
        }
    }

    virtual void AssembleFaceGrad(const Array<const FiniteElement *>&el1,
                                  const Array<const FiniteElement *>&el2,
                                  const Array<const FiniteElement *> &pel1,
                                  const Array<const FiniteElement *> &pel2,
                                  FaceElementTransformations &Tr,
                                  const Array<const Vector *> &elfun,
                                  const Array<const Vector *>&pelfun,
                                  const Array2D<DenseMatrix *> &elmats)
    {

    }

    virtual void AssemblePrmElementVector(const Array<const FiniteElement *> &el,
                                          const Array<const FiniteElement *> &pel,
                                          ElementTransformation &Tr,
                                          const Array<const Vector *> &elfun,
                                          const Array<const Vector *> &alfun,
                                          const Array<const Vector *> &pelfun,
                                          const Array<Vector *> &elvec)
    {
        int dof_u0 = el[0]->GetDof();
        int dof_r0 = pel[0]->GetDof();
        int dof_r1 = pel[1]->GetDof();

        int dim = el[0]->GetDim();

        Vector& e0 = *(elvec[0]);
        Vector& e1 = *(elvec[1]);

        e0.SetSize(dof_r0);
        e0=0.0;
        e1.SetSize(dof_r1);
        e1=0.0;

        int spaceDim = Tr.GetSpaceDim();
        if (dim != spaceDim)
        {
            mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
                             " is not defined on manifold meshes");
        }

        //shape functions
        Vector shu0(dof_u0);
        Vector shr0(dof_r0);
        Vector shr1(dof_r1);
        DenseMatrix dsu0(dof_u0,dim);
        DenseMatrix B(dof_u0, 4);
        B=0.0;

        double w;

        Vector param(2); param=0.0;
        Vector uu(4); uu=0.0;
        Vector aa(4); aa=0.0;
        Vector rr;
        Vector lvec0; lvec0.SetSize(dof_r0);
        Vector lvec1; lvec1.SetSize(dof_r1);

        const IntegrationRule *ir = nullptr;
        if(ir==nullptr){
            int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
                                +pel[0]->GetOrder()+pel[1]->GetOrder();
            ir=&IntRules.Get(Tr.GetGeometryType(),order);
        }

        for (int i = 0; i < ir->GetNPoints(); i++)
        {
            const IntegrationPoint &ip = ir->IntPoint(i);
            Tr.SetIntPoint(&ip);
            w=Tr.Weight();
            w = ip.weight * w;

            el[0]->CalcPhysDShape(Tr,dsu0);
            el[0]->CalcPhysShape(Tr,shu0);
            pel[0]->CalcPhysShape(Tr,shr0);
            pel[1]->CalcPhysShape(Tr,shr1);

            param[0]=shr0*(*pelfun[0]);
            param[1]=shr1*(*pelfun[1]);

            //set the matrix B
            for(int jj=0;jj<dim;jj++)
            {
                B.SetCol(jj,dsu0.GetColumn(jj));
            }
            B.SetCol(3,shu0);
            B.MultTranspose(*elfun[0],uu);
            B.MultTranspose(*alfun[0],aa);

            qfunc->AQResidual(Tr, ip, param, uu, aa, rr);

            lvec0=shr0;
            lvec0*=rr[0];
            lvec1=shr1;
            lvec1*=rr[1];

            e0.Add(w,lvec0);
            e1.Add(w,lvec1);
        }

    }

    virtual void AssemblePrmFaceVector(const Array<const FiniteElement *> &el1,
                                       const Array<const FiniteElement *> &el2,
                                       const Array<const FiniteElement *> &pel1,
                                       const Array<const FiniteElement *> &pel2,
                                       FaceElementTransformations &Tr,
                                       const Array<const Vector *> &elfun,
                                       const Array<const Vector *> &alfun,
                                       const Array<const Vector *> &pelfun,
                                       const Array<Vector *> &elvect)
    {

    }

private:
    LinDiffQFunc* qfunc;
};

}

int main(int argc, char *argv[])
{
    const char *mesh_file = "../../data/beam-tet.mesh";
    int ser_ref_levels = 1;
    int order = 2;
    bool visualization = true;
    double newton_rel_tol = 1e-4;
    double newton_abs_tol = 1e-6;
    int newton_iter = 10;
    int print_level = 0;

    mfem::OptionsParser args(argc, argv);
    args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
    args.AddOption(&ser_ref_levels,
                     "-rs",
                     "--refine-serial",
                     "Number of times to refine the mesh uniformly in serial.");
    args.AddOption(&order,
                      "-o",
                      "--order",
                      "Order (degree) of the finite elements.");
    args.AddOption(&visualization,
                      "-vis",
                      "--visualization",
                      "-no-vis",
                      "--no-visualization",
                      "Enable or disable GLVis visualization.");
    args.AddOption(&newton_rel_tol,
                      "-rel",
                      "--relative-tolerance",
                      "Relative tolerance for the Newton solve.");
    args.AddOption(&newton_abs_tol,
                      "-abs",
                      "--absolute-tolerance",
                      "Absolute tolerance for the Newton solve.");
    args.AddOption(&newton_iter,
                      "-it",
                      "--newton-iterations",
                      "Maximum iterations for the Newton solve.");

    args.Parse();
    if (!args.Good())
    {
        args.PrintUsage(std::cout);
        return 1;
    }
    args.PrintOptions(std::cout);
    // 3. Read the (serial) mesh from the given mesh file on all processors.  We
    //    can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
    //    with the same code.
    mfem::Mesh *mesh = new mfem::Mesh(mesh_file, 1, 1);
    int dim = mesh->Dimension();

    // 4. Refine the mesh in serial to increase the resolution. In this example
    //    we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
    //    a command-line parameter.
    for (int lev = 0; lev < ser_ref_levels; lev++)
    {
        mesh->UniformRefinement();
    }

    /// Define the q-function
    mfem::ConstantCoefficient* dc=new mfem::ConstantCoefficient(1.0);
    mfem::ConstantCoefficient* lc=new mfem::ConstantCoefficient(1.0);
    mfem::LinDiffQFunc* qfun=new mfem::LinDiffQFunc(*dc,*lc,1.0,1.0,1.0);

    mfem::H1_FECollection fec00(order, dim);
    mfem::L2_FECollection fec01(order, dim);
    mfem::FiniteElementSpace* bfes00=new mfem::FiniteElementSpace(mesh,&fec00,1,mfem::Ordering::byVDIM);
    mfem::FiniteElementSpace* pfes00=new mfem::FiniteElementSpace(mesh,&fec00,1,mfem::Ordering::byVDIM);
    mfem::FiniteElementSpace* pfes01=new mfem::FiniteElementSpace(mesh,&fec01,1,mfem::Ordering::byVDIM);

    /// Define parametric nonlinear form
    mfem::Array<mfem::FiniteElementSpace*> bfes;
    mfem::Array<mfem::FiniteElementSpace*> pfes;

    bfes.Append(bfes00);
    pfes.Append(pfes00);
    pfes.Append(pfes01);

    mfem::PrmBlockNonlinearForm* nf=new mfem::PrmBlockNonlinearForm(bfes,pfes);
    nf->AddDomainIntegrator(new mfem::PrmBlockLSFEMDiffusion(*qfun));

    /// Define the grid functions
    mfem::GridFunction* bgf00=new mfem::GridFunction(bfes00);
    mfem::GridFunction* pgf00=new mfem::GridFunction(pfes00);
    mfem::GridFunction* pgf01=new mfem::GridFunction(pfes01);
    mfem::GridFunction* ggf00=new mfem::GridFunction(pfes00);
    mfem::GridFunction* ggf01=new mfem::GridFunction(pfes01);

    *bgf00=0.0;
    *pgf00=1.0;
    *pgf01=1.0;

    mfem::BlockVector solbv; solbv.Update(nf->GetBlockTrueOffsets()); solbv=0.0;
    mfem::BlockVector resbv; resbv.Update(nf->GetBlockTrueOffsets()); resbv=0.0;
    mfem::BlockVector adjbv; adjbv.Update(nf->GetBlockTrueOffsets()); adjbv=0.0;
    mfem::BlockVector prmbv; prmbv.Update(nf->PrmGetBlockTrueOffsets()); prmbv=1.0;
    mfem::BlockVector grdbv; grdbv.Update(nf->PrmGetBlockTrueOffsets()); grdbv=0.0;

    bgf00->SetFromTrueDofs(solbv.GetBlock(0));
    pgf00->SetFromTrueDofs(prmbv.GetBlock(0));
    pgf01->SetFromTrueDofs(prmbv.GetBlock(1));

    nf->SetPrmFields(prmbv);
    double energy = nf->GetEnergy(solbv);

    nf->Mult(solbv,resbv);
    std::cout<<"Norm res="<<resbv.Norml2()<<std::endl;

    //mfem::Operator& K=nf->GetGradient(solbv);
    std::cout<<"energy ="<< energy<<std::endl;

    nf->SetStateFields(solbv);
    nf->SetAdjointFields(adjbv);
    nf->PrmMult(prmbv,grdbv);

    //set the BC for the physics
    mfem::Array<mfem::Array<int> *> ess_bdr;
    mfem::Array<mfem::Vector*>      ess_rhs;
    ess_bdr.Append(new mfem::Array<int>(mesh->bdr_attributes.Max()));
    ess_rhs.Append(nullptr);
    (*ess_bdr[0]) = 1;
    nf->SetEssentialBC(ess_bdr,ess_rhs);

    //define the solvers
    mfem::UMFPackSolver* umfsolv=new mfem::UMFPackSolver();

    mfem::GMRESSolver *gmres;
    gmres = new mfem::GMRESSolver();
    gmres->SetAbsTol(newton_abs_tol/10);
    gmres->SetRelTol(newton_rel_tol/10);
    gmres->SetMaxIter(100);
    gmres->SetPrintLevel(print_level);
    //gmres->SetPreconditioner(*prec);

    mfem::NewtonSolver *ns;
    ns = new mfem::NewtonSolver();
    ns->iterative_mode = true;
    ns->SetSolver(*gmres);
    ns->SetOperator(*nf);
    ns->SetPrintLevel(print_level);
    ns->SetRelTol(newton_rel_tol);
    ns->SetAbsTol(newton_abs_tol);
    ns->SetMaxIter(newton_iter);

    mfem::Vector b; //RHS is zero
    solbv=0.0;
    ns->Mult(b, solbv);

    nf->SetStateFields(solbv);
    nf->SetAdjointFields(solbv);
    nf->PrmMult(prmbv,grdbv);

    mfem::ParaViewDataCollection *dacol = new mfem::ParaViewDataCollection("Example91",
                                                                      mesh);

    ggf00->SetFromTrueDofs(grdbv.GetBlock(0));
    ggf01->SetFromTrueDofs(grdbv.GetBlock(1));
    pgf00->SetFromTrueDofs(solbv.GetBlock(0));

    dacol->SetLevelsOfDetail(order);
    dacol->RegisterField("sol", pgf00);
    dacol->RegisterField("grad00", ggf00);
    dacol->RegisterField("grad01", ggf01);

    dacol->SetTime(1.0);
    dacol->SetCycle(1);
    dacol->Save();

    delete dacol;

    delete ns;
    delete umfsolv;
    delete gmres;
    delete ess_bdr[0];

    delete bgf00;
    delete pgf00;
    delete pgf01;
    delete ggf00;
    delete ggf01;

    delete nf;
    delete pfes01;
    delete pfes00;
    delete bfes00;

    delete qfun;
    delete lc;
    delete dc;

    delete mesh;

}
<commit_msg>clean start<commit_after><|endoftext|>


<commit_before>/*
Copyright 2016 Fixstars Corporation

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 <stdlib.h>
#include <iostream>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <libsgm.h>

#define ASSERT_MSG(expr, msg) \
	if (!(expr)) { \
		std::cerr << msg << std::endl; \
		std::exit(EXIT_FAILURE); \
	} \

int main(int argc, char* argv[])
{
	cv::CommandLineParser parser(argc, argv,
		"{@left_img  | <none> | path to input left image                                                            }"
		"{@right_img | <none> | path to input right image                                                           }"
		"{disp_size  |     64 | maximum possible disparity value                                                    }"
		"{P1         |     10 | penalty on the disparity change by plus or minus 1 between nieghbor pixels          }"
		"{P2         |    120 | penalty on the disparity change by more than 1 between neighbor pixels              }"
		"{uniqueness |   0.95 | margin in ratio by which the best cost function value should be at least second one }"
		"{num_paths  |      8 | number of scanlines used in cost aggregation                                        }"
		"{min_disp   |      0 | minimum disparity value                                                             }"
		"{help h     |        | display this help and exit                                                          }");

	if (parser.has("help")) {
		parser.printMessage();
		return 0;
	}

	cv::Mat  left = cv::imread(parser.get<cv::String>("@left_img"), -1);
	cv::Mat right = cv::imread(parser.get<cv::String>("@right_img"), -1);

	if (!parser.check()) {
		parser.printErrors();
		parser.printMessage();
		std::exit(EXIT_FAILURE);
	}

	const int disp_size = parser.get<int>("disp_size");
	const int P1 = parser.get<int>("P1");
	const int P2 = parser.get<int>("P2");
	const float uniqueness = parser.get<float>("uniqueness");
	const int num_paths = parser.get<int>("num_paths");
	const int min_disp = parser.get<int>("min_disp");

	ASSERT_MSG(!left.empty() && !right.empty(), "imread failed.");
	ASSERT_MSG(left.size() == right.size() && left.type() == right.type(), "input images must be same size and type.");
	ASSERT_MSG(left.type() == CV_8U || left.type() == CV_16U, "input image format must be CV_8U or CV_16U.");
	ASSERT_MSG(disp_size == 64 || disp_size == 128 || disp_size == 256, "disparity size must be 64, 128 or 256.");
	ASSERT_MSG(num_paths == 4 || num_paths == 8, "number of scanlines must be 4 or 8.");

	const sgm::PathType path_type = num_paths == 8 ? sgm::PathType::SCAN_8PATH : sgm::PathType::SCAN_4PATH;
	const int input_depth = left.type() == CV_8U ? 8 : 16;
	const int output_depth = 16;

	const sgm::StereoSGM::Parameters param(P1, P2, uniqueness, false, path_type, min_disp);
	sgm::StereoSGM ssgm(left.cols, left.rows, disp_size, input_depth, output_depth, sgm::EXECUTE_INOUT_HOST2HOST, param);

	cv::Mat disparity(left.size(), CV_16S);

	ssgm.execute(left.data, right.data, disparity.data);

	// create mask for invalid disp
	cv::Mat mask = disparity == ssgm.get_invalid_disparity();

	// show image
	cv::Mat disparity_8u, disparity_color;
	disparity.convertTo(disparity_8u, CV_8U, 255. / disp_size);
	cv::applyColorMap(disparity_8u, disparity_color, cv::COLORMAP_JET);
	disparity_8u.setTo(0, mask);
	disparity_color.setTo(cv::Scalar(0, 0, 0), mask);
	if (left.type() != CV_8U)
		cv::normalize(left, left, 0, 255, cv::NORM_MINMAX, CV_8U);

	std::vector<cv::Mat> images = { disparity_8u, disparity_color, left };
	std::vector<std::string> titles = { "disparity", "disparity color", "input" };

	std::cout << "Hot keys:" << std::endl;
	std::cout << "\tESC - quit the program" << std::endl;
	std::cout << "\ts - switch display (disparity | colored disparity | input image)" << std::endl;

	int mode = 0;
	while (true) {

		cv::setWindowTitle("image", titles[mode]);
		cv::imshow("image", images[mode]);

		const char c = cv::waitKey(0);
		if (c == 's')
			mode = (mode < 2 ? mode + 1 : 0);
		if (c == 27)
			break;
	}

	return 0;
}
<commit_msg>Use LR_max_diff param at sample/image<commit_after>/*
Copyright 2016 Fixstars Corporation

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 <stdlib.h>
#include <iostream>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <libsgm.h>

#define ASSERT_MSG(expr, msg) \
	if (!(expr)) { \
		std::cerr << msg << std::endl; \
		std::exit(EXIT_FAILURE); \
	} \

int main(int argc, char* argv[])
{
	cv::CommandLineParser parser(argc, argv,
		"{@left_img   | <none> | path to input left image                                                            }"
		"{@right_img  | <none> | path to input right image                                                           }"
		"{disp_size   |     64 | maximum possible disparity value                                                    }"
		"{P1          |     10 | penalty on the disparity change by plus or minus 1 between nieghbor pixels          }"
		"{P2          |    120 | penalty on the disparity change by more than 1 between neighbor pixels              }"
		"{uniqueness  |   0.95 | margin in ratio by which the best cost function value should be at least second one }"
		"{num_paths   |      8 | number of scanlines used in cost aggregation                                        }"
		"{min_disp    |      0 | minimum disparity value                                                             }"
		"{LR_max_diff |      1 | maximum allowed difference between left and right disparity                         }"
		"{help h      |        | display this help and exit                                                          }");

	if (parser.has("help")) {
		parser.printMessage();
		return 0;
	}

	cv::Mat  left = cv::imread(parser.get<cv::String>("@left_img"), -1);
	cv::Mat right = cv::imread(parser.get<cv::String>("@right_img"), -1);

	if (!parser.check()) {
		parser.printErrors();
		parser.printMessage();
		std::exit(EXIT_FAILURE);
	}

	const int disp_size = parser.get<int>("disp_size");
	const int P1 = parser.get<int>("P1");
	const int P2 = parser.get<int>("P2");
	const float uniqueness = parser.get<float>("uniqueness");
	const int num_paths = parser.get<int>("num_paths");
	const int min_disp = parser.get<int>("min_disp");
	const int LR_max_diff = parser.get<int>("LR_max_diff");

	ASSERT_MSG(!left.empty() && !right.empty(), "imread failed.");
	ASSERT_MSG(left.size() == right.size() && left.type() == right.type(), "input images must be same size and type.");
	ASSERT_MSG(left.type() == CV_8U || left.type() == CV_16U, "input image format must be CV_8U or CV_16U.");
	ASSERT_MSG(disp_size == 64 || disp_size == 128 || disp_size == 256, "disparity size must be 64, 128 or 256.");
	ASSERT_MSG(num_paths == 4 || num_paths == 8, "number of scanlines must be 4 or 8.");

	const sgm::PathType path_type = num_paths == 8 ? sgm::PathType::SCAN_8PATH : sgm::PathType::SCAN_4PATH;
	const int input_depth = left.type() == CV_8U ? 8 : 16;
	const int output_depth = 16;

	const sgm::StereoSGM::Parameters param(P1, P2, uniqueness, false, path_type, min_disp, LR_max_diff);
	sgm::StereoSGM ssgm(left.cols, left.rows, disp_size, input_depth, output_depth, sgm::EXECUTE_INOUT_HOST2HOST, param);

	cv::Mat disparity(left.size(), CV_16S);

	ssgm.execute(left.data, right.data, disparity.data);

	// create mask for invalid disp
	cv::Mat mask = disparity == ssgm.get_invalid_disparity();

	// show image
	cv::Mat disparity_8u, disparity_color;
	disparity.convertTo(disparity_8u, CV_8U, 255. / disp_size);
	cv::applyColorMap(disparity_8u, disparity_color, cv::COLORMAP_JET);
	disparity_8u.setTo(0, mask);
	disparity_color.setTo(cv::Scalar(0, 0, 0), mask);
	if (left.type() != CV_8U)
		cv::normalize(left, left, 0, 255, cv::NORM_MINMAX, CV_8U);

	std::vector<cv::Mat> images = { disparity_8u, disparity_color, left };
	std::vector<std::string> titles = { "disparity", "disparity color", "input" };

	std::cout << "Hot keys:" << std::endl;
	std::cout << "\tESC - quit the program" << std::endl;
	std::cout << "\ts - switch display (disparity | colored disparity | input image)" << std::endl;

	int mode = 0;
	while (true) {

		cv::setWindowTitle("image", titles[mode]);
		cv::imshow("image", images[mode]);

		const char c = cv::waitKey(0);
		if (c == 's')
			mode = (mode < 2 ? mode + 1 : 0);
		if (c == 27)
			break;
	}

	return 0;
}
<|endoftext|>