<commit_before>
// (c) COPRIGHT URI/MIT 1996
// Please first read the full copyright statement in the file COPYRIGH.
//
// Authors:
//	jhrg,jimg	James Gallagher (jgallagher@gso.uri.edu)

// $Log: getdap.cc,v $
// Revision 1.2  1996/05/28 15:49:55  jimg
// Removed code that read from the stream after the das/dds parser built the
// internal object (since there was nothing in that stream after the parse
// geturl would always crash).
//
// Revision 1.1  1996/05/22 23:34:21  jimg
// First version. Built to test the new WWW code in the class Connect.
//

static char rcsid[]={"$Id: getdap.cc,v 1.2 1996/05/28 15:49:55 jimg Exp $"};

#include <stdio.h>

#include <GetOpt.h>
#include <String.h>

#include "Connect.h"

void
usage(String name)
{
    cerr << "Usage: " << name
	 << "[Ada] [v <codes>] [m <num>] <url> [<url> ...]" << endl;
    cerr << "       " << "A: Use Connect's asynchronous mode." << endl;
    cerr << "       " << "d: For each URL, get the DODS DDS." << endl;
    cerr << "       " << "a: For each URL, get the DODS DAS." << endl;
    cerr << "       " << "v: Verbose output." << endl;
    cerr << "       " << "m: Request the same URL <num> times." << endl;
    cerr << "       " << "Without A, use the synchronous mode." << endl;
    cerr << "       " << "Without d or a, print the URL." << endl;
}

bool
read_data(FILE *fp)
{
    char c;

    if (!fp) {
	cerr <<"Whoa!!! Null stream pointer." << endl;
	return false;
    }

    // Changed from a loop that used getc() to one that uses fread(). getc()
    // worked fine for transfers of text information, but *not* for binary
    // transfers. fread() will handle both.

    while (fread(&c, 1, 1, fp))
	printf("%c", c);	// stick with stdio

    return true;
}

int
main(int argc, char * argv[])
{
    FILE * fp;
    GetOpt getopt (argc, argv, "Adav:m:");
    int option_char;
    bool async = false;
    bool get_das = false;
    bool get_dds = false;
    bool verbose = false;
    bool multi = false;
    int times = 1;
    char code[16];
    int vopts = 0;

    while ((option_char = getopt()) != EOF)
	switch (option_char)
	    {
              case 'A': async = true; break;
              case 'd': get_dds = true; break;
	      case 'a': get_das = true; break;
	      case 'v':
		verbose = true;
		vopts = strlen(getopt.optarg);
		if (vopts > 15)
		    cerr << "Too many options to verbose! Ignoring..." << endl;
		else
		    strcpy(code, getopt.optarg);
		break;
	      case 'm': multi = true; times = atoi(getopt.optarg); break;
	      case 'h':
              case '?':
	      default:
		usage(argv[0]); break;
	    }

    if (verbose && vopts < 16 && vopts > 0)
	for (char c = code[vopts-1]; --vopts; c = code[vopts-1])
	    switch (c) {
	      case 'a': WWWTRACE |= SHOW_ANCHOR_TRACE; break;
	      case 'b': WWWTRACE |= SHOW_BIND_TRACE; break;
	      case 'c': WWWTRACE |= SHOW_CACHE_TRACE; break;
	      case 'l': WWWTRACE |= SHOW_SGML_TRACE; break;
	      case 'm': WWWTRACE |= SHOW_MEM_TRACE; break;
	      case 'p': WWWTRACE |= SHOW_PROTOCOL_TRACE; break;
	      case 's': WWWTRACE |= SHOW_STREAM_TRACE; break;
	      case 't': WWWTRACE |= SHOW_THREAD_TRACE; break;
	      case 'u': WWWTRACE |= SHOW_URI_TRACE; break;
	      case 'd': break;
	      default:
		cerr << "Unrecognized verbose option: " << c << endl;
		break;
	    }

    for (int i = getopt.optind; i < argc; ++i) {
	if (verbose)
	    cerr << "Fetching " << argv[i] << ":" << endl;

	String name = argv[i];
	Connect url(name);

	if (get_das) {
	    for (int j = 0; j < times; ++j) {
		if (!url.request_das())
		    exit(1);
		if (verbose)
		    cerr << "DAS:" << endl;
		url.das().print();
	    }
	}

	if (get_dds) {
	    for (int j = 0; j < times; ++j) {
		if (!url.request_dds())
		    exit(1);
		if (verbose)
		    cerr << "DDS:" << endl;
		url.dds().print();
	    }
	}

	if (!get_das && !get_dds) {
	    String url_string = argv[i];
	    for (int j = 0; j < times; ++j) {
		if (!url.fetch_url(url_string, async))
		    exit(1);
		FILE *fp = url.output();
		if (!read_data(fp))
		    exit(1);
		fclose(fp);
	    }
	}
    }
}
<commit_msg>Fixed verbose arguments.<commit_after>
// (c) COPYRIGHT URI/MIT 1996
// Please first read the full copyright statement in the file COPYRIGH.
//
// Authors:
//	jhrg,jimg	James Gallagher (jgallagher@gso.uri.edu)

// $Log: getdap.cc,v $
// Revision 1.3  1996/05/28 17:35:40  jimg
// Fixed verbose arguments.
//
// Revision 1.2  1996/05/28 15:49:55  jimg
// Removed code that read from the stream after the das/dds parser built the
// internal object (since there was nothing in that stream after the parse
// geturl would always crash).
//
// Revision 1.1  1996/05/22 23:34:21  jimg
// First version. Built to test the new WWW code in the class Connect.
//

static char rcsid[]={"$Id: getdap.cc,v 1.3 1996/05/28 17:35:40 jimg Exp $"};

#include <stdio.h>

#include <GetOpt.h>
#include <String.h>

#include "Connect.h"

void
usage(String name)
{
    cerr << "Usage: " << name
	 << "[Ada] [v <codes>] [m <num>] <url> [<url> ...]" << endl;
    cerr << "       " << "A: Use Connect's asynchronous mode." << endl;
    cerr << "       " << "d: For each URL, get the DODS DDS." << endl;
    cerr << "       " << "a: For each URL, get the DODS DAS." << endl;
    cerr << "       " << "v: Verbose output." << endl;
    cerr << "       " << "m: Request the same URL <num> times." << endl;
    cerr << "       " << "Without A, use the synchronous mode." << endl;
    cerr << "       " << "Without d or a, print the URL." << endl;
}

bool
read_data(FILE *fp)
{
    char c;

    if (!fp) {
	cerr <<"Whoa!!! Null stream pointer." << endl;
	return false;
    }

    // Changed from a loop that used getc() to one that uses fread(). getc()
    // worked fine for transfers of text information, but *not* for binary
    // transfers. fread() will handle both.

    while (fread(&c, 1, 1, fp))
	printf("%c", c);	// stick with stdio

    return true;
}

int
main(int argc, char * argv[])
{
    FILE * fp;
    GetOpt getopt (argc, argv, "Adav:m:");
    int option_char;
    bool async = false;
    bool get_das = false;
    bool get_dds = false;
    bool verbose = false;
    bool multi = false;
    int times = 1;
    char *vcode;
    int vopts = 0;

    while ((option_char = getopt()) != EOF)
	switch (option_char)
	    {
              case 'A': async = true; break;
              case 'd': get_dds = true; break;
	      case 'a': get_das = true; break;
	      case 'v':
		verbose = true;
		vopts = strlen(getopt.optarg);
		if (vopts) {
		    vcode = new char[vopts + 1];
		    strcpy(vcode, getopt.optarg);
		}
		break;
	      case 'm': multi = true; times = atoi(getopt.optarg); break;
	      case 'h':
              case '?':
	      default:
		usage(argv[0]); exit(1); break;
	    }

    char c, *cc = vcode;
    if (verbose && vopts > 0)
	while ((c = *cc++))
	    switch (c) {
	      case 'a': WWWTRACE |= SHOW_ANCHOR_TRACE; break;
	      case 'b': WWWTRACE |= SHOW_BIND_TRACE; break;
	      case 'c': WWWTRACE |= SHOW_CACHE_TRACE; break;
	      case 'l': WWWTRACE |= SHOW_SGML_TRACE; break;
	      case 'm': WWWTRACE |= SHOW_MEM_TRACE; break;
	      case 'p': WWWTRACE |= SHOW_PROTOCOL_TRACE; break;
	      case 's': WWWTRACE |= SHOW_STREAM_TRACE; break;
	      case 't': WWWTRACE |= SHOW_THREAD_TRACE; break;
	      case 'u': WWWTRACE |= SHOW_URI_TRACE; break;
	      case 'd': break;
	      default:
		cerr << "Unrecognized verbose option: `" << c << "'" << endl;
		break;
	    }

    delete vcode;

    for (int i = getopt.optind; i < argc; ++i) {
	if (verbose)
	    cerr << "Fetching " << argv[i] << ":" << endl;

	String name = argv[i];
	Connect url(name);

	if (get_das) {
	    for (int j = 0; j < times; ++j) {
		if (!url.request_das())
		    exit(1);
		if (verbose)
		    cerr << "DAS:" << endl;
		url.das().print();
	    }
	}

	if (get_dds) {
	    for (int j = 0; j < times; ++j) {
		if (!url.request_dds())
		    exit(1);
		if (verbose)
		    cerr << "DDS:" << endl;
		url.dds().print();
	    }
	}

	if (!get_das && !get_dds) {
	    String url_string = argv[i];
	    for (int j = 0; j < times; ++j) {
		if (!url.fetch_url(url_string, async))
		    exit(1);
		FILE *fp = url.output();
		if (!read_data(fp))
		    exit(1);
		fclose(fp);
	    }
	}
    }
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "test/test_main_lib.h"

#include <fstream>
#include <memory>
#include <string>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "rtc_base/checks.h"
#include "rtc_base/event_tracer.h"
#include "rtc_base/logging.h"
#include "rtc_base/ssl_adapter.h"
#include "rtc_base/ssl_stream_adapter.h"
#include "rtc_base/thread.h"
#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
#include "test/field_trial.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/perf_test.h"

#if defined(WEBRTC_WIN)
#include "rtc_base/win32_socket_init.h"
#endif

#if defined(WEBRTC_IOS)
#include "test/ios/test_support.h"

ABSL_FLAG(std::string,
          NSTreatUnknownArgumentsAsOpen,
          "",
          "Intentionally ignored flag intended for iOS simulator.");
ABSL_FLAG(std::string,
          ApplePersistenceIgnoreState,
          "",
          "Intentionally ignored flag intended for iOS simulator.");
ABSL_FLAG(
    bool,
    save_chartjson_result,
    false,
    "Store the perf results in Documents/perf_result.json in the format "
    "described by "
    "https://github.com/catapult-project/catapult/blob/master/dashboard/docs/"
    "data-format.md.");

#else

ABSL_FLAG(std::string,
          isolated_script_test_output,
          "",
          "Path to output an empty JSON file which Chromium infra requires.");

ABSL_FLAG(
    std::string,
    isolated_script_test_perf_output,
    "",
    "Path where the perf results should be stored in the JSON format described "
    "by "
    "https://github.com/catapult-project/catapult/blob/master/dashboard/docs/"
    "data-format.md.");

constexpr char kPlotAllMetrics[] = "all";
ABSL_FLAG(std::vector<std::string>,
          plot,
          {},
          "List of metrics that should be exported for plotting (if they are "
          "available). Example: psnr,ssim,encode_time. To plot all available "
          " metrics pass 'all' as flag value");

#endif

ABSL_FLAG(bool, logs, true, "print logs to stderr");
ABSL_FLAG(bool, verbose, false, "verbose logs to stderr");

ABSL_FLAG(std::string,
          trace_event,
          "",
          "Path to collect trace events (json file) for chrome://tracing. "
          "If not set, events aren't captured.");

ABSL_FLAG(std::string,
          force_fieldtrials,
          "",
          "Field trials control experimental feature code which can be forced. "
          "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/"
          " will assign the group Enable to field trial WebRTC-FooFeature.");

namespace webrtc {

namespace {

class TestMainImpl : public TestMain {
 public:
  int Init(int* argc, char* argv[]) override {
    ::testing::InitGoogleMock(argc, argv);
    absl::ParseCommandLine(*argc, argv);

    // Default to LS_INFO, even for release builds to provide better test
    // logging.
    if (rtc::LogMessage::GetLogToDebug() > rtc::LS_INFO)
      rtc::LogMessage::LogToDebug(rtc::LS_INFO);

    if (absl::GetFlag(FLAGS_verbose))
      rtc::LogMessage::LogToDebug(rtc::LS_VERBOSE);

    rtc::LogMessage::SetLogToStderr(absl::GetFlag(FLAGS_logs) ||
                                    absl::GetFlag(FLAGS_verbose));

    std::string trace_event_path = absl::GetFlag(FLAGS_trace_event);
    const bool capture_events = !trace_event_path.empty();
    if (capture_events) {
      rtc::tracing::SetupInternalTracer();
      rtc::tracing::StartInternalCapture(trace_event_path.c_str());
    }

    // InitFieldTrialsFromString stores the char*, so the char array must
    // outlive the application.
    field_trials_ = absl::GetFlag(FLAGS_force_fieldtrials);
    webrtc::field_trial::InitFieldTrialsFromString(field_trials_.c_str());
    webrtc::metrics::Enable();

#if defined(WEBRTC_WIN)
    winsock_init_ = std::make_unique<rtc::WinsockInitializer>();
#endif

    // Initialize SSL which are used by several tests.
    rtc::InitializeSSL();
    rtc::SSLStreamAdapter::EnableTimeCallbackForTesting();

    // Ensure that main thread gets wrapped as an rtc::Thread.
    // TODO(bugs.webrt.org/9714): It might be better to avoid wrapping the main
    // thread, or leave it to individual tests that need it. But as long as we
    // have automatic thread wrapping, we need this to avoid that some other
    // random thread (which one depending on which tests are run) gets
    // automatically wrapped.
    rtc::ThreadManager::Instance()->WrapCurrentThread();
    RTC_CHECK(rtc::Thread::Current());

    if (capture_events) {
      rtc::tracing::StopInternalCapture();
    }
    return 0;
  }

  int Run(int argc, char* argv[]) override {
#if defined(WEBRTC_IOS)
    rtc::test::InitTestSuite(RUN_ALL_TESTS, argc, argv,
                             absl::GetFlag(FLAGS_save_chartjson_result));
    rtc::test::RunTestsFromIOSApp();
    return 0;
#else
    int exit_code = RUN_ALL_TESTS();

    std::string chartjson_result_file =
        absl::GetFlag(FLAGS_isolated_script_test_perf_output);
    if (!chartjson_result_file.empty()) {
      webrtc::test::WritePerfResults(chartjson_result_file);
    }
    std::vector<std::string> metrics_to_plot = absl::GetFlag(FLAGS_plot);
    if (!metrics_to_plot.empty()) {
      if (metrics_to_plot.size() == 1 &&
          metrics_to_plot[0] == kPlotAllMetrics) {
        metrics_to_plot.clear();
      }
      webrtc::test::PrintPlottableResults(metrics_to_plot);
    }

    std::string result_filename =
        absl::GetFlag(FLAGS_isolated_script_test_output);
    if (!result_filename.empty()) {
      std::ofstream result_file(result_filename);
      result_file << "{\"version\": 3}";
      result_file.close();
    }

#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) ||  \
    defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
    defined(UNDEFINED_SANITIZER)
    // We want the test flagged as failed only for sanitizer defects,
    // in which case the sanitizer will override exit code with 66.
    return 0;
#endif

    return exit_code;
#endif
  }

  ~TestMainImpl() override = default;

 private:
#if defined(WEBRTC_WIN)
  std::unique_ptr<rtc::WinsockInitializer> winsock_init_;
#endif
};

}  // namespace

std::unique_ptr<TestMain> TestMain::Create() {
  return std::make_unique<TestMainImpl>();
}

}  // namespace webrtc
<commit_msg>Revert "Remove an old hack from test_main_lib.cc."<commit_after>/*
 *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "test/test_main_lib.h"

#include <fstream>
#include <memory>
#include <string>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "rtc_base/checks.h"
#include "rtc_base/event_tracer.h"
#include "rtc_base/logging.h"
#include "rtc_base/ssl_adapter.h"
#include "rtc_base/ssl_stream_adapter.h"
#include "rtc_base/thread.h"
#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
#include "test/field_trial.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/perf_test.h"

#if defined(WEBRTC_WIN)
#include "rtc_base/win32_socket_init.h"
#endif

#if defined(WEBRTC_IOS)
#include "test/ios/test_support.h"

ABSL_FLAG(std::string,
          NSTreatUnknownArgumentsAsOpen,
          "",
          "Intentionally ignored flag intended for iOS simulator.");
ABSL_FLAG(std::string,
          ApplePersistenceIgnoreState,
          "",
          "Intentionally ignored flag intended for iOS simulator.");
ABSL_FLAG(
    bool,
    save_chartjson_result,
    false,
    "Store the perf results in Documents/perf_result.json in the format "
    "described by "
    "https://github.com/catapult-project/catapult/blob/master/dashboard/docs/"
    "data-format.md.");

#else

ABSL_FLAG(std::string,
          isolated_script_test_output,
          "",
          "Path to output an empty JSON file which Chromium infra requires.");

ABSL_FLAG(
    std::string,
    isolated_script_test_perf_output,
    "",
    "Path where the perf results should be stored in the JSON format described "
    "by "
    "https://github.com/catapult-project/catapult/blob/master/dashboard/docs/"
    "data-format.md.");

constexpr char kPlotAllMetrics[] = "all";
ABSL_FLAG(std::vector<std::string>,
          plot,
          {},
          "List of metrics that should be exported for plotting (if they are "
          "available). Example: psnr,ssim,encode_time. To plot all available "
          " metrics pass 'all' as flag value");

#endif

ABSL_FLAG(bool, logs, true, "print logs to stderr");
ABSL_FLAG(bool, verbose, false, "verbose logs to stderr");

ABSL_FLAG(std::string,
          trace_event,
          "",
          "Path to collect trace events (json file) for chrome://tracing. "
          "If not set, events aren't captured.");

ABSL_FLAG(std::string,
          force_fieldtrials,
          "",
          "Field trials control experimental feature code which can be forced. "
          "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/"
          " will assign the group Enable to field trial WebRTC-FooFeature.");

namespace webrtc {

namespace {

class TestMainImpl : public TestMain {
 public:
  int Init(int* argc, char* argv[]) override {
    ::testing::InitGoogleMock(argc, argv);
    absl::ParseCommandLine(*argc, argv);

    // Default to LS_INFO, even for release builds to provide better test
    // logging.
    if (rtc::LogMessage::GetLogToDebug() > rtc::LS_INFO)
      rtc::LogMessage::LogToDebug(rtc::LS_INFO);

    if (absl::GetFlag(FLAGS_verbose))
      rtc::LogMessage::LogToDebug(rtc::LS_VERBOSE);

    rtc::LogMessage::SetLogToStderr(absl::GetFlag(FLAGS_logs) ||
                                    absl::GetFlag(FLAGS_verbose));

    std::string trace_event_path = absl::GetFlag(FLAGS_trace_event);
    const bool capture_events = !trace_event_path.empty();
    if (capture_events) {
      rtc::tracing::SetupInternalTracer();
      rtc::tracing::StartInternalCapture(trace_event_path.c_str());
    }

    // TODO(bugs.webrtc.org/9792): we need to reference something from
    // fileutils.h so that our downstream hack where we replace fileutils.cc
    // works. Otherwise the downstream flag implementation will take over and
    // botch the flag introduced by the hack. Remove this awful thing once the
    // downstream implementation has been eliminated.
    (void)webrtc::test::JoinFilename("horrible", "hack");

    // InitFieldTrialsFromString stores the char*, so the char array must
    // outlive the application.
    field_trials_ = absl::GetFlag(FLAGS_force_fieldtrials);
    webrtc::field_trial::InitFieldTrialsFromString(field_trials_.c_str());
    webrtc::metrics::Enable();

#if defined(WEBRTC_WIN)
    winsock_init_ = std::make_unique<rtc::WinsockInitializer>();
#endif

    // Initialize SSL which are used by several tests.
    rtc::InitializeSSL();
    rtc::SSLStreamAdapter::EnableTimeCallbackForTesting();

    // Ensure that main thread gets wrapped as an rtc::Thread.
    // TODO(bugs.webrt.org/9714): It might be better to avoid wrapping the main
    // thread, or leave it to individual tests that need it. But as long as we
    // have automatic thread wrapping, we need this to avoid that some other
    // random thread (which one depending on which tests are run) gets
    // automatically wrapped.
    rtc::ThreadManager::Instance()->WrapCurrentThread();
    RTC_CHECK(rtc::Thread::Current());

    if (capture_events) {
      rtc::tracing::StopInternalCapture();
    }
    return 0;
  }

  int Run(int argc, char* argv[]) override {
#if defined(WEBRTC_IOS)
    rtc::test::InitTestSuite(RUN_ALL_TESTS, argc, argv,
                             absl::GetFlag(FLAGS_save_chartjson_result));
    rtc::test::RunTestsFromIOSApp();
    return 0;
#else
    int exit_code = RUN_ALL_TESTS();

    std::string chartjson_result_file =
        absl::GetFlag(FLAGS_isolated_script_test_perf_output);
    if (!chartjson_result_file.empty()) {
      webrtc::test::WritePerfResults(chartjson_result_file);
    }
    std::vector<std::string> metrics_to_plot = absl::GetFlag(FLAGS_plot);
    if (!metrics_to_plot.empty()) {
      if (metrics_to_plot.size() == 1 &&
          metrics_to_plot[0] == kPlotAllMetrics) {
        metrics_to_plot.clear();
      }
      webrtc::test::PrintPlottableResults(metrics_to_plot);
    }

    std::string result_filename =
        absl::GetFlag(FLAGS_isolated_script_test_output);
    if (!result_filename.empty()) {
      std::ofstream result_file(result_filename);
      result_file << "{\"version\": 3}";
      result_file.close();
    }

#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) ||  \
    defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
    defined(UNDEFINED_SANITIZER)
    // We want the test flagged as failed only for sanitizer defects,
    // in which case the sanitizer will override exit code with 66.
    return 0;
#endif

    return exit_code;
#endif
  }

  ~TestMainImpl() override = default;

 private:
#if defined(WEBRTC_WIN)
  std::unique_ptr<rtc::WinsockInitializer> winsock_init_;
#endif
};

}  // namespace

std::unique_ptr<TestMain> TestMain::Create() {
  return std::make_unique<TestMainImpl>();
}

}  // namespace webrtc
<|endoftext|>


<commit_before>/*
   fskit: a library for creating multi-threaded in-RAM filesystems
   Copyright (C) 2014  Jude Nelson

   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 3 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "close.h"
#include "route.h"
#include "util.h"

// destroy a file handle
// the file handle must be write-locked
// this calls the route for close as well.
// in all cases, the file handle is destroyed, but this method will return negative if the route callback failed.
static int fskit_file_handle_destroy( struct fskit_file_handle* fh ) {

   fh->fent = NULL;

   if( fh->path ) {
      safe_free( fh->path );
      fh->path = NULL;
   }

   pthread_rwlock_destroy( &fh->lock );

   memset( fh, 0, sizeof(struct fskit_file_handle) );

   safe_free( fh );

   return 0;
}

// run the user-installed close handler
// return 0 on success, or if there are no routes
// return negative on callback failure
int fskit_run_user_close( struct fskit_core* core, char const* path, struct fskit_entry* fent, void* handle_data ) {

   // route?
   struct fskit_route_dispatch_args dargs;
   int rc = 0;
   int cbrc = 0;

   fskit_route_close_args( &dargs, handle_data );
   rc = fskit_route_call_close( core, path, fent, &dargs, &cbrc );

   if( rc == -EPERM || rc == -ENOSYS ) {

      // no routes
      return 0;
   }
   else if( cbrc != 0 ) {

      // callback failed
      return cbrc;
   }
   else {

      // success!
      return 0;
   }
}

// close a file descriptor.  This will free it, so don't use it again.  On success, the handle's app-specific data will be placed into *app_handle_data.
// a file may be destroyed by closing it, if it was unlinked.  If so, *app_file_data will contain its app-specific data.
// return 0 on success
// return -EBADF if the handle is invalid
// return -EDEADLK of there is a bug in the lock handling
int fskit_close( struct fskit_core* core, struct fskit_file_handle* fh ) {

   int rc = 0;

   rc = fskit_file_handle_wlock( fh );
   if( rc != 0 ) {
      // shouldn't happen; indicates deadlock
      fskit_error("BUG: fskit_file_handle_wlock(%p) rc = %d\n", fh, rc );
      return rc;
   }

   // validity?
   if( fh->fent == NULL ) {
      fskit_file_handle_unlock( fh );
      return -EBADF;
   }

   rc = fskit_entry_wlock( fh->fent );
   if( rc != 0 ) {
      // shouldn't happen: indicates deadlock
      fskit_error("BUG: fskit_entry_wlock(%p) rc = %d\n", fh->fent, rc );
      fskit_file_handle_unlock( fh );
      return rc;
   }

   // clean up the handle
   rc = fskit_run_user_close( core, fh->path, fh->fent, fh->app_data );
   if( rc != 0 ) {
      // failed to run user close
      fskit_error("fskit_run_user_close(%s) rc = %d\n", fh->path, rc );

      fskit_entry_unlock( fh->fent );
      fskit_file_handle_unlock( fh );
      return rc;
   }

   // no longer open by this handle
   fh->fent->open_count--;

   // see if we can destroy this....
   rc = fskit_entry_try_destroy( core, fh->path, fh->fent );
   if( rc > 0 ) {

      // fent was unlocked and destroyed
      safe_free( fh->fent );
      rc = 0;

      fskit_file_count_update( core, -1 );
   }
   else if( rc < 0 ) {

      // some error occurred
      fskit_error("fskit_entry_try_destroy(%p) rc = %d\n", fh->fent, rc );
      fskit_entry_unlock( fh->fent );

      return rc;
   }
   else {

      // done with this entry
      fskit_entry_unlock( fh->fent );
   }

   // get rid of this handle
   fskit_file_handle_unlock( fh );
   fskit_file_handle_destroy( fh );

   return rc;

}<commit_msg>Condense inode cleanup logic in close()<commit_after>/*
   fskit: a library for creating multi-threaded in-RAM filesystems
   Copyright (C) 2014  Jude Nelson

   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 3 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "close.h"
#include "route.h"
#include "util.h"

// destroy a file handle
// the file handle must be write-locked
// this calls the route for close as well.
// in all cases, the file handle is destroyed, but this method will return negative if the route callback failed.
static int fskit_file_handle_destroy( struct fskit_file_handle* fh ) {

   fh->fent = NULL;

   if( fh->path ) {
      safe_free( fh->path );
      fh->path = NULL;
   }

   pthread_rwlock_destroy( &fh->lock );

   memset( fh, 0, sizeof(struct fskit_file_handle) );

   safe_free( fh );

   return 0;
}

// run the user-installed close handler
// return 0 on success, or if there are no routes
// return negative on callback failure
int fskit_run_user_close( struct fskit_core* core, char const* path, struct fskit_entry* fent, void* handle_data ) {

   // route?
   struct fskit_route_dispatch_args dargs;
   int rc = 0;
   int cbrc = 0;

   fskit_route_close_args( &dargs, handle_data );
   rc = fskit_route_call_close( core, path, fent, &dargs, &cbrc );

   if( rc == -EPERM || rc == -ENOSYS ) {

      // no routes
      return 0;
   }
   else if( cbrc != 0 ) {

      // callback failed
      return cbrc;
   }
   else {

      // success!
      return 0;
   }
}

// close a file descriptor.  This will free it, so don't use it again.  On success, the handle's app-specific data will be placed into *app_handle_data.
// a file may be destroyed by closing it, if it was unlinked.  If so, *app_file_data will contain its app-specific data.
// return 0 on success
// return -EBADF if the handle is invalid
// return -EDEADLK of there is a bug in the lock handling
int fskit_close( struct fskit_core* core, struct fskit_file_handle* fh ) {

   int rc = 0;

   rc = fskit_file_handle_wlock( fh );
   if( rc != 0 ) {
      // shouldn't happen; indicates deadlock
      fskit_error("BUG: fskit_file_handle_wlock(%p) rc = %d\n", fh, rc );
      return rc;
   }

   // validity?
   if( fh->fent == NULL ) {
      fskit_file_handle_unlock( fh );
      return -EBADF;
   }

   rc = fskit_entry_wlock( fh->fent );
   if( rc != 0 ) {
      // shouldn't happen: indicates deadlock
      fskit_error("BUG: fskit_entry_wlock(%p) rc = %d\n", fh->fent, rc );
      fskit_file_handle_unlock( fh );
      return rc;
   }

   // clean up the handle
   rc = fskit_run_user_close( core, fh->path, fh->fent, fh->app_data );
   if( rc != 0 ) {
      // failed to run user close
      fskit_error("fskit_run_user_close(%s) rc = %d\n", fh->path, rc );

      fskit_entry_unlock( fh->fent );
      fskit_file_handle_unlock( fh );
      return rc;
   }

   // no longer open by this handle
   fh->fent->open_count--;

   rc = fskit_entry_try_destroy_and_free( core, fh->path, fh->fent );

   if( rc < 0 ) {

      // some error occurred
      fskit_error("fskit_entry_try_destroy(%p) rc = %d\n", fh->fent, rc );
      fskit_entry_unlock( fh->fent );

      return rc;
   }
   else if( rc == 0 ) {

      // done with this entry--it's still exists
      fskit_entry_unlock( fh->fent );
   }
   else {

      // destroyed and freed
      fh->fent = NULL;
      rc = 0;
   }

   // get rid of this handle
   fskit_file_handle_unlock( fh );
   fskit_file_handle_destroy( fh );

   return rc;

}<|endoftext|>


<commit_before>/*

Copyright (c) 2013, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "test.hpp"
#include "setup_transfer.hpp"
#include "dht_server.hpp"
#include "peer_server.hpp"
#include "libtorrent/alert.hpp"
#include "libtorrent/alert_types.hpp"

#include <fstream>

using namespace libtorrent;

char const* proxy_name[] = {
	"none",
	"socks4",
	"socks5",
	"socks5_pw",
	"http",
	"http_pw",
	"i2p_proxy"
};

std::vector<std::string> rejected_trackers;

bool alert_predicate(libtorrent::alert* a)
{
	anonymous_mode_alert* am = alert_cast<anonymous_mode_alert>(a);
	if (am == NULL) return false;

	if (am->kind == anonymous_mode_alert::tracker_not_anonymous)
		rejected_trackers.push_back(am->str);

	return false;
}

enum flags_t
{
	anonymous_mode = 1,
	expect_http_connection = 2,
	expect_udp_connection = 4,
	expect_http_reject = 8,
	expect_udp_reject = 16,
	expect_dht_msg = 32,
	expect_peer_connection = 64,
};

void test_proxy(proxy_settings::proxy_type proxy_type, int flags)
{
	fprintf(stderr, "\n=== TEST == proxy: %s anonymous-mode: %s\n\n", proxy_name[proxy_type], (flags & anonymous_mode) ? "yes" : "no");
	int http_port = start_web_server();
	int udp_port = start_tracker();
	int dht_port = start_dht();
	int peer_port = start_peer();

	int prev_udp_announces = g_udp_tracker_requests;
	int prev_http_announces = g_http_tracker_requests;

	int const alert_mask = alert::all_categories
		& ~alert::progress_notification
		& ~alert::stats_notification;

	session* s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48875, 49800), "0.0.0.0", 0, alert_mask);

	session_settings sett;
	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = true;
	sett.anonymous_mode = flags & anonymous_mode;
	sett.force_proxy = flags & anonymous_mode;

	// if we don't do this, the peer connection test
	// will be delayed by several seconds, by first
	// trying uTP
	sett.enable_outgoing_utp = false;
	s->set_settings(sett);

	proxy_settings ps;
	ps.hostname = "non-existing.com";
	ps.port = 4444;
	ps.type = proxy_type;
	s->set_proxy(ps);

	s->start_dht();

	error_code ec;
	create_directory("tmp1_privacy", ec);
	std::ofstream file(combine_path("tmp1_privacy", "temporary").c_str());
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	char http_tracker_url[200];
	snprintf(http_tracker_url, sizeof(http_tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(http_tracker_url, 0);

	char udp_tracker_url[200];
	snprintf(udp_tracker_url, sizeof(udp_tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(udp_tracker_url, 1);

	add_torrent_params addp;
	addp.flags &= ~add_torrent_params::flag_paused;
	addp.flags &= ~add_torrent_params::flag_auto_managed;
	addp.ti = t;
	addp.save_path = "tmp1_privacy";
	addp.dht_nodes.push_back(std::pair<std::string, int>("127.0.0.1", dht_port));
	torrent_handle h = s->add_torrent(addp);

	printf("connect_peer: 127.0.0.1:%d\n", peer_port);
	h.connect_peer(tcp::endpoint(address_v4::from_string("127.0.0.1"), peer_port));

	rejected_trackers.clear();
	for (int i = 0; i < 15; ++i)
	{
		print_alerts(*s, "s", false, false, false, &alert_predicate);
		test_sleep(100);
	}

	// we should have announced to the tracker by now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + bool(flags & expect_udp_connection));
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + bool(flags & expect_http_connection));
	if (flags & expect_dht_msg)
	{
		TEST_CHECK(num_dht_hits() > 0);
	}
	else
	{
		TEST_EQUAL(num_dht_hits(), 0);
	}

	if (flags & expect_peer_connection)
	{
		TEST_CHECK(num_peer_hits() > 0);
	}
	else
	{
		TEST_EQUAL(num_peer_hits(), 0);
	}

	if (flags & expect_udp_reject)
		TEST_CHECK(std::find(rejected_trackers.begin(), rejected_trackers.end(), udp_tracker_url) != rejected_trackers.end());

	if (flags & expect_http_reject)
		TEST_CHECK(std::find(rejected_trackers.begin(), rejected_trackers.end(), http_tracker_url) != rejected_trackers.end());

	fprintf(stderr, "%s: ~session\n", time_now_string());
	delete s;
	fprintf(stderr, "%s: ~session done\n", time_now_string());

	stop_peer();
	stop_dht();
	stop_tracker();
	stop_web_server();
}

int test_main()
{
	// not using anonymous mode
	// UDP fails open if we can't connect to the proxy
	// or if the proxy doesn't support UDP
	test_proxy(proxy_settings::none, expect_udp_connection | expect_http_connection | expect_dht_msg | expect_peer_connection);
	test_proxy(proxy_settings::socks4, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::socks5, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::socks5_pw, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::http, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::http_pw, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::i2p_proxy, expect_udp_connection | expect_dht_msg);

	// using anonymous mode

	// anonymous mode doesn't require a proxy when one isn't configured. It could be
	// used with a VPN for instance. This will all changed in 1.0, where anonymous
	// mode is separated from force_proxy
	test_proxy(proxy_settings::none, anonymous_mode | expect_peer_connection);
	test_proxy(proxy_settings::socks4, anonymous_mode | expect_udp_reject);
	test_proxy(proxy_settings::socks5, anonymous_mode);
	test_proxy(proxy_settings::socks5_pw, anonymous_mode);
	test_proxy(proxy_settings::http, anonymous_mode | expect_udp_reject);
	test_proxy(proxy_settings::http_pw, anonymous_mode | expect_udp_reject);
	test_proxy(proxy_settings::i2p_proxy, anonymous_mode);
	return 0;
}

<commit_msg>speed up the test_privacy test a bit<commit_after>/*

Copyright (c) 2013, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "test.hpp"
#include "setup_transfer.hpp"
#include "dht_server.hpp"
#include "peer_server.hpp"
#include "libtorrent/alert.hpp"
#include "libtorrent/alert_types.hpp"

#include <fstream>

using namespace libtorrent;

char const* proxy_name[] = {
	"none",
	"socks4",
	"socks5",
	"socks5_pw",
	"http",
	"http_pw",
	"i2p_proxy"
};

std::vector<std::string> rejected_trackers;

bool alert_predicate(libtorrent::alert* a)
{
	anonymous_mode_alert* am = alert_cast<anonymous_mode_alert>(a);
	if (am == NULL) return false;

	if (am->kind == anonymous_mode_alert::tracker_not_anonymous)
		rejected_trackers.push_back(am->str);

	return false;
}

enum flags_t
{
	anonymous_mode = 1,
	expect_http_connection = 2,
	expect_udp_connection = 4,
	expect_http_reject = 8,
	expect_udp_reject = 16,
	expect_dht_msg = 32,
	expect_peer_connection = 64,
};

void test_proxy(proxy_settings::proxy_type proxy_type, int flags)
{
	fprintf(stderr, "\n=== TEST == proxy: %s anonymous-mode: %s\n\n", proxy_name[proxy_type], (flags & anonymous_mode) ? "yes" : "no");
	int http_port = start_web_server();
	int udp_port = start_tracker();
	int dht_port = start_dht();
	int peer_port = start_peer();

	int prev_udp_announces = g_udp_tracker_requests;
	int prev_http_announces = g_http_tracker_requests;

	int const alert_mask = alert::all_categories
		& ~alert::progress_notification
		& ~alert::stats_notification;

	session* s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48875, 49800), "0.0.0.0", 0, alert_mask);

	session_settings sett;
	sett.stop_tracker_timeout = 1;
	sett.tracker_completion_timeout = 1;
	sett.tracker_receive_timeout = 1;
	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = true;
	sett.anonymous_mode = flags & anonymous_mode;
	sett.force_proxy = flags & anonymous_mode;

	// if we don't do this, the peer connection test
	// will be delayed by several seconds, by first
	// trying uTP
	sett.enable_outgoing_utp = false;
	s->set_settings(sett);

	proxy_settings ps;
	ps.hostname = "non-existing.com";
	ps.port = 4444;
	ps.type = proxy_type;
	s->set_proxy(ps);

	s->start_dht();

	error_code ec;
	create_directory("tmp1_privacy", ec);
	std::ofstream file(combine_path("tmp1_privacy", "temporary").c_str());
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	char http_tracker_url[200];
	snprintf(http_tracker_url, sizeof(http_tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(http_tracker_url, 0);

	char udp_tracker_url[200];
	snprintf(udp_tracker_url, sizeof(udp_tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(udp_tracker_url, 1);

	add_torrent_params addp;
	addp.flags &= ~add_torrent_params::flag_paused;
	addp.flags &= ~add_torrent_params::flag_auto_managed;
	addp.ti = t;
	addp.save_path = "tmp1_privacy";
	addp.dht_nodes.push_back(std::pair<std::string, int>("127.0.0.1", dht_port));
	torrent_handle h = s->add_torrent(addp);

	printf("connect_peer: 127.0.0.1:%d\n", peer_port);
	h.connect_peer(tcp::endpoint(address_v4::from_string("127.0.0.1"), peer_port));

	rejected_trackers.clear();
	for (int i = 0; i < 15; ++i)
	{
		print_alerts(*s, "s", false, false, false, &alert_predicate);
		test_sleep(100);

		if (g_udp_tracker_requests >= prev_udp_announces + 1
			&& g_http_tracker_requests >= prev_http_announces + 1
			&& num_peer_hits() > 0)
			break;
	}

	// we should have announced to the tracker by now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + bool(flags & expect_udp_connection));
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + bool(flags & expect_http_connection));
	if (flags & expect_dht_msg)
	{
		TEST_CHECK(num_dht_hits() > 0);
	}
	else
	{
		TEST_EQUAL(num_dht_hits(), 0);
	}

	if (flags & expect_peer_connection)
	{
		TEST_CHECK(num_peer_hits() > 0);
	}
	else
	{
		TEST_EQUAL(num_peer_hits(), 0);
	}

	if (flags & expect_udp_reject)
		TEST_CHECK(std::find(rejected_trackers.begin(), rejected_trackers.end(), udp_tracker_url) != rejected_trackers.end());

	if (flags & expect_http_reject)
		TEST_CHECK(std::find(rejected_trackers.begin(), rejected_trackers.end(), http_tracker_url) != rejected_trackers.end());

	fprintf(stderr, "%s: ~session\n", time_now_string());
	delete s;
	fprintf(stderr, "%s: ~session done\n", time_now_string());

	stop_peer();
	stop_dht();
	stop_tracker();
	stop_web_server();
}

int test_main()
{
	// not using anonymous mode
	// UDP fails open if we can't connect to the proxy
	// or if the proxy doesn't support UDP
	test_proxy(proxy_settings::none, expect_udp_connection | expect_http_connection | expect_dht_msg | expect_peer_connection);
	test_proxy(proxy_settings::socks4, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::socks5, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::socks5_pw, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::http, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::http_pw, expect_udp_connection | expect_dht_msg);
	test_proxy(proxy_settings::i2p_proxy, expect_udp_connection | expect_dht_msg);

	// using anonymous mode

	// anonymous mode doesn't require a proxy when one isn't configured. It could be
	// used with a VPN for instance. This will all changed in 1.0, where anonymous
	// mode is separated from force_proxy
	test_proxy(proxy_settings::none, anonymous_mode | expect_peer_connection);
	test_proxy(proxy_settings::socks4, anonymous_mode | expect_udp_reject);
	test_proxy(proxy_settings::socks5, anonymous_mode);
	test_proxy(proxy_settings::socks5_pw, anonymous_mode);
	test_proxy(proxy_settings::http, anonymous_mode | expect_udp_reject);
	test_proxy(proxy_settings::http_pw, anonymous_mode | expect_udp_reject);
	test_proxy(proxy_settings::i2p_proxy, anonymous_mode);
	return 0;
}

<|endoftext|>


<commit_before>#include "test.hpp"
#include "setup_transfer.hpp"
#include "libtorrent/alert.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/error_code.hpp"

#include <fstream>

using namespace libtorrent;

int test_main()
{
	int http_port = start_web_server();
	int udp_port = start_tracker();

	int prev_udp_announces = g_udp_tracker_requests;
	int prev_http_announces = g_http_tracker_requests;

	int const alert_mask = alert::all_categories
		& ~alert::progress_notification
		& ~alert::stats_notification;

	session* s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48875, 49800), "0.0.0.0", 0, alert_mask);

	session_settings sett;
	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = true;
	s->set_settings(sett);

	error_code ec;
	create_directory("./tmp1_tracker", ec);
	std::ofstream file("./tmp1_tracker/temporary");
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	char tracker_url[200];
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(tracker_url);

	snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(tracker_url);

	add_torrent_params addp;
	addp.paused = false;
	addp.auto_managed = false;
	addp.ti = t;
	addp.save_path = "./tmp1_tracker";
	torrent_handle h = s->add_torrent(addp);

	test_sleep(2000);

	// we should have announced to the tracker by now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 1);

	delete s;

	// we should have announced the stopped event now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 2);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 2);

	// ========================================
	// test that we move on to try the next tier if the first one fails
	// ========================================

	s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(39775, 39800), "0.0.0.0", 0, alert_mask);

	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = false;
	sett.tracker_completion_timeout = 4;
	sett.tracker_receive_timeout = 2;
	s->set_settings(sett);

	create_directory("./tmp2_tracker", ec);
	file.open("./tmp2_tracker/temporary");
	t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	// this should fail
	snprintf(tracker_url, sizeof(tracker_url), "udp://www.google.com:80/announce");
	t->add_tracker(tracker_url, 0);

	// and this should fail
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.2:3/announce");
	t->add_tracker(tracker_url, 1);

	// this should be announced to
	// udp trackers are prioritized if they're on the same host as an http one
	// so this must be before the http one on 127.0.0.1
	snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(tracker_url, 2);

	// and this should not be announced to (since the one before it succeeded)
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(tracker_url, 3);

	prev_udp_announces = g_udp_tracker_requests;
	prev_http_announces = g_http_tracker_requests;

	addp.paused = false;
	addp.auto_managed = false;
	addp.ti = t;
	addp.save_path = "./tmp2_tracker";
	h = s->add_torrent(addp);

	for (int i = 0; i < 10; ++i)
	{
		test_sleep(1000);
		print_alerts(*s, "s");
		if (g_udp_tracker_requests == prev_udp_announces + 1) break;
	}

	test_sleep(1000);

	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces);

	delete s;

	stop_tracker();
	stop_web_server();

	return 0;
}

<commit_msg>fix unit test<commit_after>#include "test.hpp"
#include "setup_transfer.hpp"
#include "libtorrent/alert.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/error_code.hpp"

#include <fstream>

using namespace libtorrent;

int test_main()
{
	int http_port = start_web_server();
	int udp_port = start_tracker();

	int prev_udp_announces = g_udp_tracker_requests;
	int prev_http_announces = g_http_tracker_requests;

	int const alert_mask = alert::all_categories
		& ~alert::progress_notification
		& ~alert::stats_notification;

	session* s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48875, 49800), "0.0.0.0", 0, alert_mask);

	session_settings sett;
	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = true;
	s->set_settings(sett);

	error_code ec;
	create_directory("./tmp1_tracker", ec);
	std::ofstream file("./tmp1_tracker/temporary");
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	char tracker_url[200];
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(tracker_url);

	snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(tracker_url);

	add_torrent_params addp;
	addp.paused = false;
	addp.auto_managed = false;
	addp.ti = t;
	addp.save_path = "./tmp1_tracker";
	torrent_handle h = s->add_torrent(addp);

	test_sleep(2000);

	// we should have announced to the tracker by now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 1);

	delete s;

	// we should have announced the stopped event now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 2);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 2);

	// ========================================
	// test that we move on to try the next tier if the first one fails
	// ========================================

	s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(39775, 39800), "0.0.0.0", 0, alert_mask);

	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = false;
	sett.tracker_completion_timeout = 2;
	sett.tracker_receive_timeout = 1;
	s->set_settings(sett);

	create_directory("./tmp2_tracker", ec);
	file.open("./tmp2_tracker/temporary");
	t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	// this should fail
	snprintf(tracker_url, sizeof(tracker_url), "udp://www.google.com:80/announce");
	t->add_tracker(tracker_url, 0);

	// and this should fail
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.2:3/announce");
	t->add_tracker(tracker_url, 1);

	// this should be announced to
	// udp trackers are prioritized if they're on the same host as an http one
	// so this must be before the http one on 127.0.0.1
	snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(tracker_url, 2);

	// and this should not be announced to (since the one before it succeeded)
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(tracker_url, 3);

	prev_udp_announces = g_udp_tracker_requests;
	prev_http_announces = g_http_tracker_requests;

	addp.paused = false;
	addp.auto_managed = false;
	addp.ti = t;
	addp.save_path = "./tmp2_tracker";
	h = s->add_torrent(addp);

	for (int i = 0; i < 10; ++i)
	{
		print_alerts(*s, "s");
		test_sleep(1000);
		if (g_udp_tracker_requests == prev_udp_announces + 1) break;
	}

	test_sleep(1000);

	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces);

	delete s;

	stop_tracker();
	stop_web_server();

	return 0;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

void merge(int arr[], int l, int m, int r) {
    int i, j, k;
    int n1 = m - l + 1;
    int n2 =  r - m;

    int L[n1], R[n2];
    for (i = 0; i < n1; i++) { L[i] = arr[l + i]; }
    for (j = 0; j < n2; j++) { R[j] = arr[m + 1+ j]; }

    i = 0; j = 0; k = l;

    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        } k++;
    }

    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }

    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(int arr[], int l, int r) {
    if (l < r) {
        int m = l+(r-l)/2;
        mergeSort(arr, l, m);
        mergeSort(arr, m+1, r);
        merge(arr, l, m, r);
    }
}

void printA(int A[], int n) {
  cout << endl;
  for (int i = 0; i < n-1; i++) {
    cout << A[i] << ", ";
  } cout << A[n-1];
  cout << endl;
}

int main(int argc, char const *argv[]) {
  srand(time(0));

  cout << "Please input size of array to sort" << endl << ">  ";
  int size; cin >> size;
  int * A = new int[size];

  for (int i = 0; i < size; i++) {
    A[i] = (rand() % 1000000);
  }

  cout << "Initial array:";
  printA(A, size);

  mergeSort(A, 0, size - 1);

  cout << endl << "Sorted array:";
  printA(A, size);

  return 0;
}
<commit_msg>Update merge.cpp<commit_after>#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

void merge(int arr[], int l, int m, int r) {
    int i, j, k;
    int n1 = m - l + 1;
    int n2 =  r - m;

    int L[n1], R[n2];
    for (i = 0; i < n1; i++) { L[i] = arr[l + i]; }
    for (j = 0; j < n2; j++) { R[j] = arr[m + 1+ j]; }

    i = 0; j = 0; k = l;

    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        } k++;
    }

    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }

    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(int arr[], int l, int r) {
    if (l < r) {
        int m = l+(r-l)/2;
        mergeSort(arr, l, m);
        mergeSort(arr, m+1, r);
        merge(arr, l, m, r);
    }
}

void printA(int A[], int n) {
    cout << endl;
    for (int i = 0; i < n-1; i++) {
        cout << A[i] << ", ";
    } cout << A[n-1];
    cout << endl;
}

int main(int argc, char const *argv[]) {
    srand(time(0));

    cout << "Please input size of array to sort" << endl << ">  ";
    int size; cin >> size;
    int * A = new int[size];

    for (int i = 0; i < size; i++) {
        A[i] = (rand() % 1000000);
    }

    cout << "Initial array:";
    printA(A, size);

    mergeSort(A, 0, size - 1);

    cout << endl << "Sorted array:";
    printA(A, size);

    return 0;
}
<|endoftext|>


<commit_before>#include "libs/base/http_server.h"
#include "libs/base/ipc_m7.h"
#include "libs/base/mutex.h"
#include "libs/base/strings.h"
#include "libs/base/utils.h"
#include "libs/posenet/posenet.h"
#include "libs/tasks/CameraTask/camera_task.h"
#include "libs/tasks/EdgeTpuTask/edgetpu_task.h"
#include "libs/tpu/edgetpu_manager.h"
#include "third_party/freertos_kernel/include/FreeRTOS.h"
#include "third_party/freertos_kernel/include/semphr.h"
#include "third_party/freertos_kernel/include/task.h"
#include "third_party/mjson/src/mjson.h"
#include "third_party/tflite-micro/tensorflow/lite/micro/micro_interpreter.h"

#if defined(OOBE_DEMO_ETHERNET)
#include "libs/base/ethernet.h"
#endif  // defined(OOBE_DEMO_ETHERNET)

#if defined(OOBE_DEMO_WIFI)
#include "libs/base/gpio.h"
#include "libs/base/wifi.h"
#endif  // defined(OOBE_DEMO_WIFI)

#include <cstdio>
#include <cstring>
#include <list>
#include <vector>

namespace valiant {
namespace oobe {
namespace {
constexpr float kThreshold = 0.4;

constexpr int kCmdStart = 1;
constexpr int kCmdStop = 2;
constexpr int kCmdProcess = 3;

SemaphoreHandle_t camera_output_mtx;
std::vector<uint8_t> camera_output;

SemaphoreHandle_t posenet_output_mtx;
std::unique_ptr<valiant::posenet::Output> posenet_output;
TickType_t posenet_output_time;

std::vector<uint8_t> CreatePoseJson(const posenet::Output& output,
                                    float threshold) {
    std::vector<uint8_t> s;
    s.reserve(2048);

    int num_appended_poses = 0;
    StrAppend(&s, "[");
    for (int i = 0; i < output.num_poses; ++i) {
        if (output.poses[i].score < threshold) continue;

        StrAppend(&s, num_appended_poses != 0 ? ",\n" : "");
        StrAppend(&s, "{\n");
        StrAppend(&s, "  \"score\": %g,\n", output.poses[i].score);
        StrAppend(&s, "  \"keypoints\": [\n");
        for (int j = 0; j < posenet::kKeypoints; ++j) {
            const auto& kp = output.poses[i].keypoints[j];
            StrAppend(&s, "    [%g, %g, %g]", kp.score, kp.x, kp.y);
            StrAppend(&s, j != posenet::kKeypoints - 1 ? ",\n" : "\n");
        }
        StrAppend(&s, "  ]\n");
        StrAppend(&s, "}");
        ++num_appended_poses;
    }
    StrAppend(&s, "]");
    return s;
}

HttpServer::Content UriHandler(const char *name) {
  if (std::strcmp(name, "/camera") == 0) {
    valiant::MutexLock lock(camera_output_mtx);
    if (camera_output.empty()) return {};
    return valiant::HttpServer::Content{std::move(camera_output)};
  }

  if (std::strcmp(name, "/pose") == 0) {
    valiant::MutexLock lock(posenet_output_mtx);
    if (!posenet_output) return {};
    auto json = CreatePoseJson(*posenet_output, kThreshold);
    posenet_output.reset();
    return json;
  }

  return {};
}

void HandleAppMessage(
    const uint8_t data[valiant::ipc::kMessageBufferDataSize], void *param) {
  (void)data;
  vTaskResume(reinterpret_cast<TaskHandle_t>(param));
}

struct TaskMessage {
  int command;
  SemaphoreHandle_t completion_semaphore;
  void *data;
};

class OOBETask {
 public:
  OOBETask(const char *name) : queue_(xQueueCreate(2, sizeof(TaskMessage))) {
    xTaskCreate(StaticTaskFunc, name, configMINIMAL_STACK_SIZE * 30, this,
                APP_TASK_PRIORITY, &task_);
  }

  void Start() { SendCommandBlocking(kCmdStart); }

  void Stop() { SendCommandBlocking(kCmdStop); }

 protected:
  virtual void TaskFunc() = 0;
  QueueHandle_t queue_;

 private:
  static void StaticTaskFunc(void *param) {
    auto thiz = reinterpret_cast<OOBETask *>(param);
    thiz->TaskFunc();
  }

  void SendCommandBlocking(int command) {
    TaskMessage message = {command, xSemaphoreCreateBinary()};
    xQueueSend(queue_, &message, portMAX_DELAY);
    xSemaphoreTake(message.completion_semaphore, portMAX_DELAY);
    vSemaphoreDelete(message.completion_semaphore);
  }

  TaskHandle_t task_;
};

class PosenetTask : public OOBETask {
 public:
  PosenetTask() : OOBETask("posenet_task"){};
  void QueueFrame(std::vector<uint8_t> *frame) {
    TaskMessage message = {kCmdProcess, nullptr, frame};
    xQueueSend(queue_, &message, portMAX_DELAY);
  }

 protected:
  void TaskFunc() override {
    TaskMessage message;
    valiant::posenet::Output output;
    while (true) {
      xQueueReceive(queue_, &message, portMAX_DELAY);

      switch (message.command) {
        case kCmdStart:
          configASSERT(!started_);
          started_ = true;
          printf("Posenet: started\r\n");
          break;
        case kCmdStop:
          configASSERT(started_);
          started_ = false;
          printf("Posenet: stopped\r\n");
          break;
        case kCmdProcess: {
          configASSERT(started_);
          auto camera_frame =
              reinterpret_cast<std::vector<uint8_t> *>(message.data);
          TfLiteTensor *input = valiant::posenet::input();
          memcpy(tflite::GetTensorData<uint8_t>(input), camera_frame->data(),
                 camera_frame->size());
          delete camera_frame;

          valiant::posenet::loop(&output, false);
          int good_poses_count = 0;
          for (int i = 0; i < valiant::posenet::kPoses; ++i) {
            if (output.poses[i].score > kThreshold) {
              good_poses_count++;
            }
          }
          if (good_poses_count) {
            valiant::MutexLock lock(posenet_output_mtx);
            posenet_output = std::make_unique<valiant::posenet::Output>(output);
            posenet_output_time = xTaskGetTickCount();
          }
        } break;

        default:
          printf("Unknown command: %d\r\n", message.command);
          break;
      }

      // Signal the command completion semaphore, if present.
      if (message.completion_semaphore) {
        xSemaphoreGive(message.completion_semaphore);
      }
    }
  }

 private:
  bool started_ = false;
};

class CameraTask : public OOBETask {
 public:
  CameraTask(PosenetTask *posenet_task)
      : OOBETask("camera_task"), posenet_task_(posenet_task){};

 protected:
  void TaskFunc() override {
    TaskMessage message;
    while (true) {
      xQueueReceive(queue_, &message, portMAX_DELAY);

      switch (message.command) {
        case kCmdStart:
          configASSERT(!started_);
          started_ = true;
          valiant::CameraTask::GetSingleton()->Enable(
              valiant::camera::Mode::STREAMING);
          printf("Camera: started\r\n");
          posenet_task_->Start();
          QueueProcess();
          break;
        case kCmdStop:
          configASSERT(started_);
          valiant::CameraTask::GetSingleton()->Disable();
          started_ = false;
          printf("Camera: stopped\r\n");
          posenet_task_->Stop();
          break;
        case kCmdProcess: {
          if (!started_) {
            continue;
          }

          std::vector<uint8_t> input(valiant::posenet::kPosenetSize);
          valiant::camera::FrameFormat fmt;
          fmt.width = valiant::posenet::kPosenetWidth;
          fmt.height = valiant::posenet::kPosenetHeight;
          fmt.fmt = valiant::camera::Format::RGB;
          fmt.filter = valiant::camera::FilterMethod::BILINEAR;
          fmt.preserve_ratio = false;
          fmt.buffer = input.data();
          valiant::CameraTask::GetFrame({fmt});

          {
            valiant::MutexLock lock(camera_output_mtx);
            camera_output = std::move(input);
          }

          // Signal posenet.
          posenet_task_->QueueFrame(new std::vector<uint8_t>(camera_output));

          // Process next camera frame.
          QueueProcess();
        } break;

        default:
          printf("Unknown command: %d\r\n", message.command);
          break;
      }

      // Signal the command completion semaphore, if present.
      if (message.completion_semaphore) {
        xSemaphoreGive(message.completion_semaphore);
      }
    }
  }

 private:
  void QueueProcess() {
    TaskMessage message = {kCmdProcess};
    xQueueSend(queue_, &message, portMAX_DELAY);
  }

  PosenetTask *posenet_task_ = nullptr;
  bool started_ = false;
};

void Main() {
  PosenetTask posenet_task;
  CameraTask camera_task(&posenet_task);
  camera_output_mtx = xSemaphoreCreateMutex();
  posenet_output_mtx = xSemaphoreCreateMutex();

// For the OOBE Demo, bring up WiFi and Ethernet. For now these are active
// but unused.
#if defined(OOBE_DEMO_ETHERNET)
  valiant::InitializeEthernet(false);
#elif defined(OOBE_DEMO_WIFI)
  valiant::TurnOnWiFi();
  if (!valiant::ConnectToWiFi()) {
    // If connecting to wi-fi fails, turn our LEDs on solid, and halt.
    valiant::gpio::SetGpio(valiant::gpio::Gpio::kPowerLED, true);
    valiant::gpio::SetGpio(valiant::gpio::Gpio::kUserLED, true);
    valiant::gpio::SetGpio(valiant::gpio::Gpio::kTpuLED, true);
    vTaskSuspend(nullptr);
  }
#endif  // defined(OOBE_DEMO_ETHERNET)

  valiant::HttpServer http_server;
  http_server.AddUriHandler(UriHandler);
  http_server.AddUriHandler(valiant::FileSystemUriHandler{});
  valiant::UseHttpServer(&http_server);

  valiant::IPCM7::GetSingleton()->RegisterAppMessageHandler(
      HandleAppMessage, xTaskGetCurrentTaskHandle());
  valiant::EdgeTpuTask::GetSingleton()->SetPower(true);
  valiant::EdgeTpuManager::GetSingleton()->OpenDevice(
      valiant::PerformanceMode::kMax);
  if (!valiant::posenet::setup()) {
    printf("setup() failed\r\n");
    vTaskSuspend(nullptr);
  }

  valiant::IPCM7::GetSingleton()->StartM4();

#if defined(OOBE_DEMO)
  int count = 0;
#endif  // defined (OOBE_DEMO)

  vTaskSuspend(nullptr);
  while (true) {
    printf("CM7 awoken\r\n");

    // Start camera_task processing, which will start posenet_task.
    camera_task.Start();
    posenet_output_time = xTaskGetTickCount();

    while (true) {
// For OOBE Demo, run 20 iterations of this loop - each contains a one
// second delay. For normal OOBE, check that the posenet task hasn't
// progressed for 5 seconds (i.e. no poses detected).
#if defined(OOBE_DEMO)
      if (count >= 20) {
        count = 0;
        break;
      }
      ++count;
      printf("M7 %d\r\n", count);
#else
      TickType_t now = xTaskGetTickCount();
      if ((now - posenet_output_time) > pdMS_TO_TICKS(5000)) {
        break;
      }
#endif  // defined(OOBE_DEMO)

      vTaskDelay(pdMS_TO_TICKS(1000));
    }

    printf("Transition back to M4\r\n");

    // Stop camera_task processing. This will also stop posenet_task.
    camera_task.Stop();

    valiant::ipc::Message msg;
    msg.type = valiant::ipc::MessageType::APP;
    valiant::IPCM7::GetSingleton()->SendMessage(msg);
    vTaskSuspend(nullptr);
  }
}
}  // namespace
}  // namespace oobe
}  // namespace valiant

extern "C" void app_main(void *param) {
  (void)param;
  valiant::oobe::Main();
  vTaskSuspend(nullptr);
}
<commit_msg>Add OOBE stats web page<commit_after>#include "libs/base/http_server.h"
#include "libs/base/ipc_m7.h"
#include "libs/base/mutex.h"
#include "libs/base/strings.h"
#include "libs/base/utils.h"
#include "libs/posenet/posenet.h"
#include "libs/tasks/CameraTask/camera_task.h"
#include "libs/tasks/EdgeTpuTask/edgetpu_task.h"
#include "libs/tpu/edgetpu_manager.h"
#include "third_party/freertos_kernel/include/FreeRTOS.h"
#include "third_party/freertos_kernel/include/semphr.h"
#include "third_party/freertos_kernel/include/task.h"
#include "third_party/mjson/src/mjson.h"
#include "third_party/tflite-micro/tensorflow/lite/micro/micro_interpreter.h"

#if defined(OOBE_DEMO_ETHERNET)
#include "libs/base/ethernet.h"
#endif  // defined(OOBE_DEMO_ETHERNET)

#if defined(OOBE_DEMO_WIFI)
#include "libs/base/gpio.h"
#include "libs/base/wifi.h"
#endif  // defined(OOBE_DEMO_WIFI)

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <list>
#include <numeric>
#include <vector>

namespace valiant {
namespace oobe {
namespace {
constexpr float kThreshold = 0.4;

constexpr int kCmdStart = 1;
constexpr int kCmdStop = 2;
constexpr int kCmdProcess = 3;

SemaphoreHandle_t camera_output_mtx;
std::vector<uint8_t> camera_output;

SemaphoreHandle_t posenet_output_mtx;
std::unique_ptr<valiant::posenet::Output> posenet_output;
TickType_t posenet_output_time;

std::vector<uint8_t> CreatePoseJson(const posenet::Output& output,
                                    float threshold) {
    std::vector<uint8_t> s;
    s.reserve(2048);

    int num_appended_poses = 0;
    StrAppend(&s, "[");
    for (int i = 0; i < output.num_poses; ++i) {
        if (output.poses[i].score < threshold) continue;

        StrAppend(&s, num_appended_poses != 0 ? ",\n" : "");
        StrAppend(&s, "{\n");
        StrAppend(&s, "  \"score\": %g,\n", output.poses[i].score);
        StrAppend(&s, "  \"keypoints\": [\n");
        for (int j = 0; j < posenet::kKeypoints; ++j) {
            const auto& kp = output.poses[i].keypoints[j];
            StrAppend(&s, "    [%g, %g, %g]", kp.score, kp.x, kp.y);
            StrAppend(&s, j != posenet::kKeypoints - 1 ? ",\n" : "\n");
        }
        StrAppend(&s, "  ]\n");
        StrAppend(&s, "}");
        ++num_appended_poses;
    }
    StrAppend(&s, "]");
    return s;
}

void GenerateStatsHtml(std::vector<uint8_t>* html) {
    std::vector<TaskStatus_t> infos(uxTaskGetNumberOfTasks());
    unsigned long total_runtime;
    auto n = uxTaskGetSystemState(infos.data(), infos.size(), &total_runtime);
    std::vector<int> indices(infos.size());
    std::iota(std::begin(indices), std::end(indices), 0);
    std::sort(std::begin(indices), std::end(indices), [&infos](int i, int j) {
        return infos[i].ulRunTimeCounter > infos[j].ulRunTimeCounter;
    });

    StrAppend(html, "<!DOCTYPE html>\r\n");
    StrAppend(html, "\r\n");
    StrAppend(html, "\r\n"),
    StrAppend(html, "<title>Run-time statistics</title>\r\n"),
    StrAppend(html, "  \r\n"),
    StrAppend(html, "\r\n"),
    StrAppend(html, "\r\n");
    StrAppend(html, "
\r\n");
    StrAppend(html, "
Task	Abs Time	% Time
\r\n");
    for (auto i = 0u; i < n; ++i) {
        const auto& info = infos[indices[i]];
        auto name = info.pcTaskName;
        auto runtime = info.ulRunTimeCounter;
        auto percent = runtime / (total_runtime / 100);
        if (percent > 0) {
            StrAppend(html,
                      "
%s	%d	%d%%
\r\n",
                      name, runtime, percent);
        } else {
            StrAppend(html,
                      "
%s	%d	<1%%
\r\n",
                      name, runtime);
        }
    }
    StrAppend(html, "
\r\n");
    StrAppend(html, "\r\n");
}

HttpServer::Content UriHandler(const char *name) {
  if (std::strcmp(name, "/stats.html") == 0) {
    std::vector<uint8_t> html;
    html.reserve(2048);
    GenerateStatsHtml(&html);
    return html;
  }

  if (std::strcmp(name, "/camera") == 0) {
    valiant::MutexLock lock(camera_output_mtx);
    if (camera_output.empty()) return {};
    return valiant::HttpServer::Content{std::move(camera_output)};
  }

  if (std::strcmp(name, "/pose") == 0) {
    valiant::MutexLock lock(posenet_output_mtx);
    if (!posenet_output) return {};
    auto json = CreatePoseJson(*posenet_output, kThreshold);
    posenet_output.reset();
    return json;
  }

  return {};
}

void HandleAppMessage(
    const uint8_t data[valiant::ipc::kMessageBufferDataSize], void *param) {
  (void)data;
  vTaskResume(reinterpret_cast<TaskHandle_t>(param));
}

struct TaskMessage {
  int command;
  SemaphoreHandle_t completion_semaphore;
  void *data;
};

class OOBETask {
 public:
  OOBETask(const char *name) : queue_(xQueueCreate(2, sizeof(TaskMessage))) {
    xTaskCreate(StaticTaskFunc, name, configMINIMAL_STACK_SIZE * 30, this,
                APP_TASK_PRIORITY, &task_);
  }

  void Start() { SendCommandBlocking(kCmdStart); }

  void Stop() { SendCommandBlocking(kCmdStop); }

 protected:
  virtual void TaskFunc() = 0;
  QueueHandle_t queue_;

 private:
  static void StaticTaskFunc(void *param) {
    auto thiz = reinterpret_cast<OOBETask *>(param);
    thiz->TaskFunc();
  }

  void SendCommandBlocking(int command) {
    TaskMessage message = {command, xSemaphoreCreateBinary()};
    xQueueSend(queue_, &message, portMAX_DELAY);
    xSemaphoreTake(message.completion_semaphore, portMAX_DELAY);
    vSemaphoreDelete(message.completion_semaphore);
  }

  TaskHandle_t task_;
};

class PosenetTask : public OOBETask {
 public:
  PosenetTask() : OOBETask("posenet_task"){};
  void QueueFrame(std::vector<uint8_t> *frame) {
    TaskMessage message = {kCmdProcess, nullptr, frame};
    xQueueSend(queue_, &message, portMAX_DELAY);
  }

 protected:
  void TaskFunc() override {
    TaskMessage message;
    valiant::posenet::Output output;
    while (true) {
      xQueueReceive(queue_, &message, portMAX_DELAY);

      switch (message.command) {
        case kCmdStart:
          configASSERT(!started_);
          started_ = true;
          printf("Posenet: started\r\n");
          break;
        case kCmdStop:
          configASSERT(started_);
          started_ = false;
          printf("Posenet: stopped\r\n");
          break;
        case kCmdProcess: {
          configASSERT(started_);
          auto camera_frame =
              reinterpret_cast<std::vector<uint8_t> *>(message.data);
          TfLiteTensor *input = valiant::posenet::input();
          memcpy(tflite::GetTensorData<uint8_t>(input), camera_frame->data(),
                 camera_frame->size());
          delete camera_frame;

          valiant::posenet::loop(&output, false);
          int good_poses_count = 0;
          for (int i = 0; i < valiant::posenet::kPoses; ++i) {
            if (output.poses[i].score > kThreshold) {
              good_poses_count++;
            }
          }
          if (good_poses_count) {
            valiant::MutexLock lock(posenet_output_mtx);
            posenet_output = std::make_unique<valiant::posenet::Output>(output);
            posenet_output_time = xTaskGetTickCount();
          }
        } break;

        default:
          printf("Unknown command: %d\r\n", message.command);
          break;
      }

      // Signal the command completion semaphore, if present.
      if (message.completion_semaphore) {
        xSemaphoreGive(message.completion_semaphore);
      }
    }
  }

 private:
  bool started_ = false;
};

class CameraTask : public OOBETask {
 public:
  CameraTask(PosenetTask *posenet_task)
      : OOBETask("camera_task"), posenet_task_(posenet_task){};

 protected:
  void TaskFunc() override {
    TaskMessage message;
    while (true) {
      xQueueReceive(queue_, &message, portMAX_DELAY);

      switch (message.command) {
        case kCmdStart:
          configASSERT(!started_);
          started_ = true;
          valiant::CameraTask::GetSingleton()->Enable(
              valiant::camera::Mode::STREAMING);
          printf("Camera: started\r\n");
          posenet_task_->Start();
          QueueProcess();
          break;
        case kCmdStop:
          configASSERT(started_);
          valiant::CameraTask::GetSingleton()->Disable();
          started_ = false;
          printf("Camera: stopped\r\n");
          posenet_task_->Stop();
          break;
        case kCmdProcess: {
          if (!started_) {
            continue;
          }

          std::vector<uint8_t> input(valiant::posenet::kPosenetSize);
          valiant::camera::FrameFormat fmt;
          fmt.width = valiant::posenet::kPosenetWidth;
          fmt.height = valiant::posenet::kPosenetHeight;
          fmt.fmt = valiant::camera::Format::RGB;
          fmt.filter = valiant::camera::FilterMethod::BILINEAR;
          fmt.preserve_ratio = false;
          fmt.buffer = input.data();
          valiant::CameraTask::GetFrame({fmt});

          {
            valiant::MutexLock lock(camera_output_mtx);
            camera_output = std::move(input);
          }

          // Signal posenet.
          posenet_task_->QueueFrame(new std::vector<uint8_t>(camera_output));

          // Process next camera frame.
          QueueProcess();
        } break;

        default:
          printf("Unknown command: %d\r\n", message.command);
          break;
      }

      // Signal the command completion semaphore, if present.
      if (message.completion_semaphore) {
        xSemaphoreGive(message.completion_semaphore);
      }
    }
  }

 private:
  void QueueProcess() {
    TaskMessage message = {kCmdProcess};
    xQueueSend(queue_, &message, portMAX_DELAY);
  }

  PosenetTask *posenet_task_ = nullptr;
  bool started_ = false;
};

void Main() {
  PosenetTask posenet_task;
  CameraTask camera_task(&posenet_task);
  camera_output_mtx = xSemaphoreCreateMutex();
  posenet_output_mtx = xSemaphoreCreateMutex();

// For the OOBE Demo, bring up WiFi and Ethernet. For now these are active
// but unused.
#if defined(OOBE_DEMO_ETHERNET)
  valiant::InitializeEthernet(false);
#elif defined(OOBE_DEMO_WIFI)
  valiant::TurnOnWiFi();
  if (!valiant::ConnectToWiFi()) {
    // If connecting to wi-fi fails, turn our LEDs on solid, and halt.
    valiant::gpio::SetGpio(valiant::gpio::Gpio::kPowerLED, true);
    valiant::gpio::SetGpio(valiant::gpio::Gpio::kUserLED, true);
    valiant::gpio::SetGpio(valiant::gpio::Gpio::kTpuLED, true);
    vTaskSuspend(nullptr);
  }
#endif  // defined(OOBE_DEMO_ETHERNET)

  valiant::HttpServer http_server;
  http_server.AddUriHandler(UriHandler);
  http_server.AddUriHandler(valiant::FileSystemUriHandler{});
  valiant::UseHttpServer(&http_server);

  valiant::IPCM7::GetSingleton()->RegisterAppMessageHandler(
      HandleAppMessage, xTaskGetCurrentTaskHandle());
  valiant::EdgeTpuTask::GetSingleton()->SetPower(true);
  valiant::EdgeTpuManager::GetSingleton()->OpenDevice(
      valiant::PerformanceMode::kMax);
  if (!valiant::posenet::setup()) {
    printf("setup() failed\r\n");
    vTaskSuspend(nullptr);
  }

  valiant::IPCM7::GetSingleton()->StartM4();

#if defined(OOBE_DEMO)
  int count = 0;
#endif  // defined (OOBE_DEMO)

  vTaskSuspend(nullptr);
  while (true) {
    printf("CM7 awoken\r\n");

    // Start camera_task processing, which will start posenet_task.
    camera_task.Start();
    posenet_output_time = xTaskGetTickCount();

    while (true) {
// For OOBE Demo, run 20 iterations of this loop - each contains a one
// second delay. For normal OOBE, check that the posenet task hasn't
// progressed for 5 seconds (i.e. no poses detected).
#if defined(OOBE_DEMO)
      if (count >= 20) {
        count = 0;
        break;
      }
      ++count;
      printf("M7 %d\r\n", count);
#else
      TickType_t now = xTaskGetTickCount();
      if ((now - posenet_output_time) > pdMS_TO_TICKS(5000)) {
        break;
      }
#endif  // defined(OOBE_DEMO)

      vTaskDelay(pdMS_TO_TICKS(1000));
    }

    printf("Transition back to M4\r\n");

    // Stop camera_task processing. This will also stop posenet_task.
    camera_task.Stop();

    valiant::ipc::Message msg;
    msg.type = valiant::ipc::MessageType::APP;
    valiant::IPCM7::GetSingleton()->SendMessage(msg);
    vTaskSuspend(nullptr);
  }
}
}  // namespace
}  // namespace oobe
}  // namespace valiant

extern "C" void app_main(void *param) {
  (void)param;
  valiant::oobe::Main();
  vTaskSuspend(nullptr);
}
<|endoftext|>