<commit_before>/*
 * Copyright 2014 Facebook, Inc.
 *
 * 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 <folly/Subprocess.h>

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>

#include <boost/container/flat_set.hpp>
#include <glog/logging.h>
#include <gtest/gtest.h>

#include <folly/Exception.h>
#include <folly/Format.h>
#include <folly/FileUtil.h>
#include <folly/String.h>
#include <folly/gen/Base.h>
#include <folly/gen/File.h>
#include <folly/gen/String.h>
#include <folly/experimental/io/FsUtil.h>

using namespace folly;

TEST(SimpleSubprocessTest, ExitsSuccessfully) {
  Subprocess proc(std::vector<std::string>{ "/bin/true" });
  EXPECT_EQ(0, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ExitsSuccessfullyChecked) {
  Subprocess proc(std::vector<std::string>{ "/bin/true" });
  proc.waitChecked();
}

TEST(SimpleSubprocessTest, ExitsWithError) {
  Subprocess proc(std::vector<std::string>{ "/bin/false" });
  EXPECT_EQ(1, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ExitsWithErrorChecked) {
  Subprocess proc(std::vector<std::string>{ "/bin/false" });
  EXPECT_THROW(proc.waitChecked(), CalledProcessError);
}

#define EXPECT_SPAWN_ERROR(err, errMsg, cmd, ...) \
  do { \
    try { \
      Subprocess proc(std::vector<std::string>{ (cmd), ## __VA_ARGS__ }); \
      ADD_FAILURE() << "expected an error when running " << (cmd); \
    } catch (const SubprocessSpawnError& ex) { \
      EXPECT_EQ((err), ex.errnoValue()); \
      if (StringPiece(ex.what()).find(errMsg) == StringPiece::npos) { \
        ADD_FAILURE() << "failed to find \"" << (errMsg) << \
          "\" in exception: \"" << ex.what() << "\""; \
      } \
    } \
  } while (0)

TEST(SimpleSubprocessTest, ExecFails) {
  EXPECT_SPAWN_ERROR(ENOENT, "failed to execute /no/such/file:",
                     "/no/such/file");
  EXPECT_SPAWN_ERROR(EACCES, "failed to execute /etc/passwd:",
                     "/etc/passwd");
  EXPECT_SPAWN_ERROR(ENOTDIR, "failed to execute /etc/passwd/not/a/file:",
                     "/etc/passwd/not/a/file");
}

TEST(SimpleSubprocessTest, ShellExitsSuccesssfully) {
  Subprocess proc("true");
  EXPECT_EQ(0, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ShellExitsWithError) {
  Subprocess proc("false");
  EXPECT_EQ(1, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ChangeChildDirectorySuccessfully) {
  // The filesystem root normally lacks a 'true' binary
  EXPECT_EQ(0, chdir("/"));
  EXPECT_SPAWN_ERROR(ENOENT, "failed to execute ./true", "./true");
  // The child can fix that by moving to /bin before exec().
  Subprocess proc("./true", Subprocess::Options().chdir("/bin"));
  EXPECT_EQ(0, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ChangeChildDirectoryWithError) {
  try {
    Subprocess proc(
      std::vector<std::string>{"/bin/true"},
      Subprocess::Options().chdir("/usually/this/is/not/a/valid/directory/")
    );
    ADD_FAILURE() << "expected to fail when changing the child's directory";
  } catch (const SubprocessSpawnError& ex) {
    EXPECT_EQ(ENOENT, ex.errnoValue());
    const std::string expectedError =
      "error preparing to execute /bin/true: No such file or directory";
    if (StringPiece(ex.what()).find(expectedError) == StringPiece::npos) {
      ADD_FAILURE() << "failed to find \"" << expectedError <<
        "\" in exception: \"" << ex.what() << "\"";
    }
  }
}

namespace {
boost::container::flat_set<int> getOpenFds() {
  auto pid = getpid();
  auto dirname = to<std::string>("/proc/", pid, "/fd");

  boost::container::flat_set<int> fds;
  for (fs::directory_iterator it(dirname);
       it != fs::directory_iterator();
       ++it) {
    int fd = to<int>(it->path().filename().native());
    fds.insert(fd);
  }
  return fds;
}

template<class Runnable>
void checkFdLeak(const Runnable& r) {
  // Get the currently open fds.  Check that they are the same both before and
  // after calling the specified function.  We read the open fds from /proc.
  // (If we wanted to work even on systems that don't have /proc, we could
  // perhaps create and immediately close a socket both before and after
  // running the function, and make sure we got the same fd number both times.)
  auto fdsBefore = getOpenFds();
  r();
  auto fdsAfter = getOpenFds();
  EXPECT_EQ(fdsAfter.size(), fdsBefore.size());
}
}

// Make sure Subprocess doesn't leak any file descriptors
TEST(SimpleSubprocessTest, FdLeakTest) {
  // Normal execution
  checkFdLeak([] {
    Subprocess proc("true");
    EXPECT_EQ(0, proc.wait().exitStatus());
  });
  // Normal execution with pipes
  checkFdLeak([] {
    Subprocess proc("echo foo; echo bar >&2",
                    Subprocess::pipeStdout() | Subprocess::pipeStderr());
    auto p = proc.communicate();
    EXPECT_EQ("foo\n", p.first);
    EXPECT_EQ("bar\n", p.second);
    proc.waitChecked();
  });

  // Test where the exec call fails()
  checkFdLeak([] {
    EXPECT_SPAWN_ERROR(ENOENT, "failed to execute", "/no/such/file");
  });
  // Test where the exec call fails() with pipes
  checkFdLeak([] {
    try {
      Subprocess proc(std::vector<std::string>({"/no/such/file"}),
                      Subprocess::pipeStdout().stderr(Subprocess::PIPE));
      ADD_FAILURE() << "expected an error when running /no/such/file";
    } catch (const SubprocessSpawnError& ex) {
      EXPECT_EQ(ENOENT, ex.errnoValue());
    }
  });
}

TEST(ParentDeathSubprocessTest, ParentDeathSignal) {
  // Find out where we are.
  static constexpr size_t pathLength = 2048;
  char buf[pathLength + 1];
  int r = readlink("/proc/self/exe", buf, pathLength);
  CHECK_ERR(r);
  buf[r] = '\0';

  fs::path helper(buf);
  helper.remove_filename();
  helper /= "subprocess_test_parent_death_helper";

  fs::path tempFile(fs::temp_directory_path() / fs::unique_path());

  std::vector<std::string> args {helper.string(), tempFile.string()};
  Subprocess proc(args);
  // The helper gets killed by its child, see details in
  // SubprocessTestParentDeathHelper.cpp
  ASSERT_EQ(SIGKILL, proc.wait().killSignal());

  // Now wait for the file to be created, see details in
  // SubprocessTestParentDeathHelper.cpp
  while (!fs::exists(tempFile)) {
    usleep(20000);  // 20ms
  }

  fs::remove(tempFile);
}

TEST(PopenSubprocessTest, PopenRead) {
  Subprocess proc("ls /", Subprocess::pipeStdout());
  int found = 0;
  gen::byLine(File(proc.stdout())) |
    [&] (StringPiece line) {
      if (line == "etc" || line == "bin" || line == "usr") {
        ++found;
      }
    };
  EXPECT_EQ(3, found);
  proc.waitChecked();
}

TEST(CommunicateSubprocessTest, SimpleRead) {
  Subprocess proc(std::vector<std::string>{ "/bin/echo", "-n", "foo", "bar"},
                  Subprocess::pipeStdout());
  auto p = proc.communicate();
  EXPECT_EQ("foo bar", p.first);
  proc.waitChecked();
}

TEST(CommunicateSubprocessTest, BigWrite) {
  const int numLines = 1 << 20;
  std::string line("hello\n");
  std::string data;
  data.reserve(numLines * line.size());
  for (int i = 0; i < numLines; ++i) {
    data.append(line);
  }

  Subprocess proc("wc -l", Subprocess::pipeStdin() | Subprocess::pipeStdout());
  auto p = proc.communicate(data);
  EXPECT_EQ(folly::format("{}\n", numLines).str(), p.first);
  proc.waitChecked();
}

TEST(CommunicateSubprocessTest, Duplex) {
  // Take 10MB of data and pass them through a filter.
  // One line, as tr is line-buffered
  const int bytes = 10 << 20;
  std::string line(bytes, 'x');

  Subprocess proc("tr a-z A-Z",
                  Subprocess::pipeStdin() | Subprocess::pipeStdout());
  auto p = proc.communicate(line);
  EXPECT_EQ(bytes, p.first.size());
  EXPECT_EQ(std::string::npos, p.first.find_first_not_of('X'));
  proc.waitChecked();
}

TEST(CommunicateSubprocessTest, ProcessGroupLeader) {
  const auto testIsLeader = "test $(cut -d ' ' -f 5 /proc/$$/stat) == $$";
  Subprocess nonLeader(testIsLeader);
  EXPECT_THROW(nonLeader.waitChecked(), CalledProcessError);
  Subprocess leader(testIsLeader, Subprocess::Options().processGroupLeader());
  leader.waitChecked();
}

TEST(CommunicateSubprocessTest, Duplex2) {
  checkFdLeak([] {
    // Pipe 200,000 lines through sed
    const size_t numCopies = 100000;
    auto iobuf = IOBuf::copyBuffer("this is a test\nanother line\n");
    IOBufQueue input;
    for (size_t n = 0; n < numCopies; ++n) {
      input.append(iobuf->clone());
    }

    std::vector<std::string> cmd({
      "sed", "-u",
      "-e", "s/a test/a successful test/",
      "-e", "/^another line/w/dev/stderr",
    });
    auto options = Subprocess::pipeStdin().pipeStdout().pipeStderr().usePath();
    Subprocess proc(cmd, options);
    auto out = proc.communicateIOBuf(std::move(input));
    proc.waitChecked();

    // Convert stdout and stderr to strings so we can call split() on them.
    fbstring stdoutStr;
    if (out.first.front()) {
      stdoutStr = out.first.move()->moveToFbString();
    }
    fbstring stderrStr;
    if (out.second.front()) {
      stderrStr = out.second.move()->moveToFbString();
    }

    // stdout should be a copy of stdin, with "a test" replaced by
    // "a successful test"
    std::vector<StringPiece> stdoutLines;
    split('\n', stdoutStr, stdoutLines);
    EXPECT_EQ(numCopies * 2 + 1, stdoutLines.size());
    // Strip off the trailing empty line
    if (!stdoutLines.empty()) {
      EXPECT_EQ("", stdoutLines.back());
      stdoutLines.pop_back();
    }
    size_t linenum = 0;
    for (const auto& line : stdoutLines) {
      if ((linenum & 1) == 0) {
        EXPECT_EQ("this is a successful test", line);
      } else {
        EXPECT_EQ("another line", line);
      }
      ++linenum;
    }

    // stderr should only contain the lines containing "another line"
    std::vector<StringPiece> stderrLines;
    split('\n', stderrStr, stderrLines);
    EXPECT_EQ(numCopies + 1, stderrLines.size());
    // Strip off the trailing empty line
    if (!stderrLines.empty()) {
      EXPECT_EQ("", stderrLines.back());
      stderrLines.pop_back();
    }
    for (const auto& line : stderrLines) {
      EXPECT_EQ("another line", line);
    }
  });
}

namespace {

bool readToString(int fd, std::string& buf, size_t maxSize) {
  size_t bytesRead = 0;

  buf.resize(maxSize);
  char* dest = &buf.front();
  size_t remaining = maxSize;

  ssize_t n = -1;
  while (remaining) {
    n = ::read(fd, dest, remaining);
    if (n == -1) {
      if (errno == EINTR) {
        continue;
      }
      if (errno == EAGAIN) {
        break;
      }
      PCHECK("read failed");
    } else if (n == 0) {
      break;
    }
    dest += n;
    remaining -= n;
  }

  buf.resize(dest - buf.data());
  return (n == 0);
}

}  // namespace

TEST(CommunicateSubprocessTest, Chatty) {
  checkFdLeak([] {
    const int lineCount = 1000;

    int wcount = 0;
    int rcount = 0;

    auto options = Subprocess::pipeStdin().pipeStdout().pipeStderr().usePath();
    std::vector<std::string> cmd {
      "sed",
      "-u",
      "-e",
      "s/a test/a successful test/",
    };

    Subprocess proc(cmd, options);

    auto writeCallback = [&] (int pfd, int cfd) -> bool {
      EXPECT_EQ(0, cfd);  // child stdin
      EXPECT_EQ(rcount, wcount);  // chatty, one read for every write

      auto msg = folly::to<std::string>("a test ", wcount, "\n");

      // Not entirely kosher, we should handle partial writes, but this is
      // fine for writes <= PIPE_BUF
      EXPECT_EQ(msg.size(), writeFull(pfd, msg.data(), msg.size()));

      ++wcount;
      proc.enableNotifications(0, false);

      return (wcount == lineCount);
    };

    bool eofSeen = false;

    auto readCallback = [&] (int pfd, int cfd) -> bool {
      std::string lineBuf;

      if (cfd != 1) {
        EXPECT_EQ(2, cfd);
        EXPECT_TRUE(readToString(pfd, lineBuf, 1));
        EXPECT_EQ(0, lineBuf.size());
        return true;
      }

      EXPECT_FALSE(eofSeen);

      std::string expected;

      if (rcount < lineCount) {
        expected = folly::to<std::string>("a successful test ", rcount++, "\n");
      }

      EXPECT_EQ(wcount, rcount);

      // Not entirely kosher, we should handle partial reads, but this is
      // fine for reads <= PIPE_BUF
      bool atEof = readToString(pfd, lineBuf, expected.size() + 1);
      if (atEof) {
        // EOF only expected after we finished reading
        EXPECT_EQ(lineCount, rcount);
        eofSeen = true;
      }

      EXPECT_EQ(expected, lineBuf);

      if (wcount != lineCount) {  // still more to write...
        proc.enableNotifications(0, true);
      }

      return eofSeen;
    };

    proc.communicate(readCallback, writeCallback);

    EXPECT_EQ(lineCount, wcount);
    EXPECT_EQ(lineCount, rcount);
    EXPECT_TRUE(eofSeen);

    EXPECT_EQ(0, proc.wait().exitStatus());
  });
}
<commit_msg>Fix incorrect 'test' statement in unit test<commit_after>/*
 * Copyright 2014 Facebook, Inc.
 *
 * 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 <folly/Subprocess.h>

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>

#include <boost/container/flat_set.hpp>
#include <glog/logging.h>
#include <gtest/gtest.h>

#include <folly/Exception.h>
#include <folly/Format.h>
#include <folly/FileUtil.h>
#include <folly/String.h>
#include <folly/gen/Base.h>
#include <folly/gen/File.h>
#include <folly/gen/String.h>
#include <folly/experimental/io/FsUtil.h>

using namespace folly;

TEST(SimpleSubprocessTest, ExitsSuccessfully) {
  Subprocess proc(std::vector<std::string>{ "/bin/true" });
  EXPECT_EQ(0, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ExitsSuccessfullyChecked) {
  Subprocess proc(std::vector<std::string>{ "/bin/true" });
  proc.waitChecked();
}

TEST(SimpleSubprocessTest, ExitsWithError) {
  Subprocess proc(std::vector<std::string>{ "/bin/false" });
  EXPECT_EQ(1, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ExitsWithErrorChecked) {
  Subprocess proc(std::vector<std::string>{ "/bin/false" });
  EXPECT_THROW(proc.waitChecked(), CalledProcessError);
}

#define EXPECT_SPAWN_ERROR(err, errMsg, cmd, ...) \
  do { \
    try { \
      Subprocess proc(std::vector<std::string>{ (cmd), ## __VA_ARGS__ }); \
      ADD_FAILURE() << "expected an error when running " << (cmd); \
    } catch (const SubprocessSpawnError& ex) { \
      EXPECT_EQ((err), ex.errnoValue()); \
      if (StringPiece(ex.what()).find(errMsg) == StringPiece::npos) { \
        ADD_FAILURE() << "failed to find \"" << (errMsg) << \
          "\" in exception: \"" << ex.what() << "\""; \
      } \
    } \
  } while (0)

TEST(SimpleSubprocessTest, ExecFails) {
  EXPECT_SPAWN_ERROR(ENOENT, "failed to execute /no/such/file:",
                     "/no/such/file");
  EXPECT_SPAWN_ERROR(EACCES, "failed to execute /etc/passwd:",
                     "/etc/passwd");
  EXPECT_SPAWN_ERROR(ENOTDIR, "failed to execute /etc/passwd/not/a/file:",
                     "/etc/passwd/not/a/file");
}

TEST(SimpleSubprocessTest, ShellExitsSuccesssfully) {
  Subprocess proc("true");
  EXPECT_EQ(0, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ShellExitsWithError) {
  Subprocess proc("false");
  EXPECT_EQ(1, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ChangeChildDirectorySuccessfully) {
  // The filesystem root normally lacks a 'true' binary
  EXPECT_EQ(0, chdir("/"));
  EXPECT_SPAWN_ERROR(ENOENT, "failed to execute ./true", "./true");
  // The child can fix that by moving to /bin before exec().
  Subprocess proc("./true", Subprocess::Options().chdir("/bin"));
  EXPECT_EQ(0, proc.wait().exitStatus());
}

TEST(SimpleSubprocessTest, ChangeChildDirectoryWithError) {
  try {
    Subprocess proc(
      std::vector<std::string>{"/bin/true"},
      Subprocess::Options().chdir("/usually/this/is/not/a/valid/directory/")
    );
    ADD_FAILURE() << "expected to fail when changing the child's directory";
  } catch (const SubprocessSpawnError& ex) {
    EXPECT_EQ(ENOENT, ex.errnoValue());
    const std::string expectedError =
      "error preparing to execute /bin/true: No such file or directory";
    if (StringPiece(ex.what()).find(expectedError) == StringPiece::npos) {
      ADD_FAILURE() << "failed to find \"" << expectedError <<
        "\" in exception: \"" << ex.what() << "\"";
    }
  }
}

namespace {
boost::container::flat_set<int> getOpenFds() {
  auto pid = getpid();
  auto dirname = to<std::string>("/proc/", pid, "/fd");

  boost::container::flat_set<int> fds;
  for (fs::directory_iterator it(dirname);
       it != fs::directory_iterator();
       ++it) {
    int fd = to<int>(it->path().filename().native());
    fds.insert(fd);
  }
  return fds;
}

template<class Runnable>
void checkFdLeak(const Runnable& r) {
  // Get the currently open fds.  Check that they are the same both before and
  // after calling the specified function.  We read the open fds from /proc.
  // (If we wanted to work even on systems that don't have /proc, we could
  // perhaps create and immediately close a socket both before and after
  // running the function, and make sure we got the same fd number both times.)
  auto fdsBefore = getOpenFds();
  r();
  auto fdsAfter = getOpenFds();
  EXPECT_EQ(fdsAfter.size(), fdsBefore.size());
}
}

// Make sure Subprocess doesn't leak any file descriptors
TEST(SimpleSubprocessTest, FdLeakTest) {
  // Normal execution
  checkFdLeak([] {
    Subprocess proc("true");
    EXPECT_EQ(0, proc.wait().exitStatus());
  });
  // Normal execution with pipes
  checkFdLeak([] {
    Subprocess proc("echo foo; echo bar >&2",
                    Subprocess::pipeStdout() | Subprocess::pipeStderr());
    auto p = proc.communicate();
    EXPECT_EQ("foo\n", p.first);
    EXPECT_EQ("bar\n", p.second);
    proc.waitChecked();
  });

  // Test where the exec call fails()
  checkFdLeak([] {
    EXPECT_SPAWN_ERROR(ENOENT, "failed to execute", "/no/such/file");
  });
  // Test where the exec call fails() with pipes
  checkFdLeak([] {
    try {
      Subprocess proc(std::vector<std::string>({"/no/such/file"}),
                      Subprocess::pipeStdout().stderr(Subprocess::PIPE));
      ADD_FAILURE() << "expected an error when running /no/such/file";
    } catch (const SubprocessSpawnError& ex) {
      EXPECT_EQ(ENOENT, ex.errnoValue());
    }
  });
}

TEST(ParentDeathSubprocessTest, ParentDeathSignal) {
  // Find out where we are.
  static constexpr size_t pathLength = 2048;
  char buf[pathLength + 1];
  int r = readlink("/proc/self/exe", buf, pathLength);
  CHECK_ERR(r);
  buf[r] = '\0';

  fs::path helper(buf);
  helper.remove_filename();
  helper /= "subprocess_test_parent_death_helper";

  fs::path tempFile(fs::temp_directory_path() / fs::unique_path());

  std::vector<std::string> args {helper.string(), tempFile.string()};
  Subprocess proc(args);
  // The helper gets killed by its child, see details in
  // SubprocessTestParentDeathHelper.cpp
  ASSERT_EQ(SIGKILL, proc.wait().killSignal());

  // Now wait for the file to be created, see details in
  // SubprocessTestParentDeathHelper.cpp
  while (!fs::exists(tempFile)) {
    usleep(20000);  // 20ms
  }

  fs::remove(tempFile);
}

TEST(PopenSubprocessTest, PopenRead) {
  Subprocess proc("ls /", Subprocess::pipeStdout());
  int found = 0;
  gen::byLine(File(proc.stdout())) |
    [&] (StringPiece line) {
      if (line == "etc" || line == "bin" || line == "usr") {
        ++found;
      }
    };
  EXPECT_EQ(3, found);
  proc.waitChecked();
}

TEST(CommunicateSubprocessTest, SimpleRead) {
  Subprocess proc(std::vector<std::string>{ "/bin/echo", "-n", "foo", "bar"},
                  Subprocess::pipeStdout());
  auto p = proc.communicate();
  EXPECT_EQ("foo bar", p.first);
  proc.waitChecked();
}

TEST(CommunicateSubprocessTest, BigWrite) {
  const int numLines = 1 << 20;
  std::string line("hello\n");
  std::string data;
  data.reserve(numLines * line.size());
  for (int i = 0; i < numLines; ++i) {
    data.append(line);
  }

  Subprocess proc("wc -l", Subprocess::pipeStdin() | Subprocess::pipeStdout());
  auto p = proc.communicate(data);
  EXPECT_EQ(folly::format("{}\n", numLines).str(), p.first);
  proc.waitChecked();
}

TEST(CommunicateSubprocessTest, Duplex) {
  // Take 10MB of data and pass them through a filter.
  // One line, as tr is line-buffered
  const int bytes = 10 << 20;
  std::string line(bytes, 'x');

  Subprocess proc("tr a-z A-Z",
                  Subprocess::pipeStdin() | Subprocess::pipeStdout());
  auto p = proc.communicate(line);
  EXPECT_EQ(bytes, p.first.size());
  EXPECT_EQ(std::string::npos, p.first.find_first_not_of('X'));
  proc.waitChecked();
}

TEST(CommunicateSubprocessTest, ProcessGroupLeader) {
  const auto testIsLeader = "test $(cut -d ' ' -f 5 /proc/$$/stat) = $$";
  Subprocess nonLeader(testIsLeader);
  EXPECT_THROW(nonLeader.waitChecked(), CalledProcessError);
  Subprocess leader(testIsLeader, Subprocess::Options().processGroupLeader());
  leader.waitChecked();
}

TEST(CommunicateSubprocessTest, Duplex2) {
  checkFdLeak([] {
    // Pipe 200,000 lines through sed
    const size_t numCopies = 100000;
    auto iobuf = IOBuf::copyBuffer("this is a test\nanother line\n");
    IOBufQueue input;
    for (size_t n = 0; n < numCopies; ++n) {
      input.append(iobuf->clone());
    }

    std::vector<std::string> cmd({
      "sed", "-u",
      "-e", "s/a test/a successful test/",
      "-e", "/^another line/w/dev/stderr",
    });
    auto options = Subprocess::pipeStdin().pipeStdout().pipeStderr().usePath();
    Subprocess proc(cmd, options);
    auto out = proc.communicateIOBuf(std::move(input));
    proc.waitChecked();

    // Convert stdout and stderr to strings so we can call split() on them.
    fbstring stdoutStr;
    if (out.first.front()) {
      stdoutStr = out.first.move()->moveToFbString();
    }
    fbstring stderrStr;
    if (out.second.front()) {
      stderrStr = out.second.move()->moveToFbString();
    }

    // stdout should be a copy of stdin, with "a test" replaced by
    // "a successful test"
    std::vector<StringPiece> stdoutLines;
    split('\n', stdoutStr, stdoutLines);
    EXPECT_EQ(numCopies * 2 + 1, stdoutLines.size());
    // Strip off the trailing empty line
    if (!stdoutLines.empty()) {
      EXPECT_EQ("", stdoutLines.back());
      stdoutLines.pop_back();
    }
    size_t linenum = 0;
    for (const auto& line : stdoutLines) {
      if ((linenum & 1) == 0) {
        EXPECT_EQ("this is a successful test", line);
      } else {
        EXPECT_EQ("another line", line);
      }
      ++linenum;
    }

    // stderr should only contain the lines containing "another line"
    std::vector<StringPiece> stderrLines;
    split('\n', stderrStr, stderrLines);
    EXPECT_EQ(numCopies + 1, stderrLines.size());
    // Strip off the trailing empty line
    if (!stderrLines.empty()) {
      EXPECT_EQ("", stderrLines.back());
      stderrLines.pop_back();
    }
    for (const auto& line : stderrLines) {
      EXPECT_EQ("another line", line);
    }
  });
}

namespace {

bool readToString(int fd, std::string& buf, size_t maxSize) {
  size_t bytesRead = 0;

  buf.resize(maxSize);
  char* dest = &buf.front();
  size_t remaining = maxSize;

  ssize_t n = -1;
  while (remaining) {
    n = ::read(fd, dest, remaining);
    if (n == -1) {
      if (errno == EINTR) {
        continue;
      }
      if (errno == EAGAIN) {
        break;
      }
      PCHECK("read failed");
    } else if (n == 0) {
      break;
    }
    dest += n;
    remaining -= n;
  }

  buf.resize(dest - buf.data());
  return (n == 0);
}

}  // namespace

TEST(CommunicateSubprocessTest, Chatty) {
  checkFdLeak([] {
    const int lineCount = 1000;

    int wcount = 0;
    int rcount = 0;

    auto options = Subprocess::pipeStdin().pipeStdout().pipeStderr().usePath();
    std::vector<std::string> cmd {
      "sed",
      "-u",
      "-e",
      "s/a test/a successful test/",
    };

    Subprocess proc(cmd, options);

    auto writeCallback = [&] (int pfd, int cfd) -> bool {
      EXPECT_EQ(0, cfd);  // child stdin
      EXPECT_EQ(rcount, wcount);  // chatty, one read for every write

      auto msg = folly::to<std::string>("a test ", wcount, "\n");

      // Not entirely kosher, we should handle partial writes, but this is
      // fine for writes <= PIPE_BUF
      EXPECT_EQ(msg.size(), writeFull(pfd, msg.data(), msg.size()));

      ++wcount;
      proc.enableNotifications(0, false);

      return (wcount == lineCount);
    };

    bool eofSeen = false;

    auto readCallback = [&] (int pfd, int cfd) -> bool {
      std::string lineBuf;

      if (cfd != 1) {
        EXPECT_EQ(2, cfd);
        EXPECT_TRUE(readToString(pfd, lineBuf, 1));
        EXPECT_EQ(0, lineBuf.size());
        return true;
      }

      EXPECT_FALSE(eofSeen);

      std::string expected;

      if (rcount < lineCount) {
        expected = folly::to<std::string>("a successful test ", rcount++, "\n");
      }

      EXPECT_EQ(wcount, rcount);

      // Not entirely kosher, we should handle partial reads, but this is
      // fine for reads <= PIPE_BUF
      bool atEof = readToString(pfd, lineBuf, expected.size() + 1);
      if (atEof) {
        // EOF only expected after we finished reading
        EXPECT_EQ(lineCount, rcount);
        eofSeen = true;
      }

      EXPECT_EQ(expected, lineBuf);

      if (wcount != lineCount) {  // still more to write...
        proc.enableNotifications(0, true);
      }

      return eofSeen;
    };

    proc.communicate(readCallback, writeCallback);

    EXPECT_EQ(lineCount, wcount);
    EXPECT_EQ(lineCount, rcount);
    EXPECT_TRUE(eofSeen);

    EXPECT_EQ(0, proc.wait().exitStatus());
  });
}
<|endoftext|>


<commit_before>#include "df_font.h"
#include "df_window.h"
#include "fonts/df_mono.h"

#include <math.h>
#include <stdlib.h>

// Based on https://physics.weber.edu/schroeder/fluids/

struct Cell {
    float n0;
    float nN, nS, nE, nW;
    float nNE, nSE, nNW, nSW;
    float rho, ux, uy;
    bool barrier;
};

enum { X_DIM = 400 };
enum { Y_DIM = 160 };
enum { RENDER_SCALE = 3 }; // Width of cell in pixels.
enum { NUM_TRACERS = 144 };

// Index into this 2D array using x + y*X_DIM, traversing rows first and then columns.
static Cell g_cells[X_DIM * Y_DIM];
static const float fluid_speed = 0.1f;
static const float viscosity = 0.02f;
static const float four9ths = 4.0 / 9.0;					// abbreviations
static const float one9th = 1.0 / 9.0;
static const float one36th = 1.0 / 36.0;
float tracers_x[NUM_TRACERS];
float tracers_y[NUM_TRACERS];

static Cell *cell(int x, int y) {
    return &g_cells[x + y * X_DIM];
}

// Set all densities in a cell to their equilibrium values.
void SetEquilibrium(int x, int y) {
    float new_ux = fluid_speed;
    float new_rho = 1.0;
    float ux3 = 3 * new_ux;
    float uy3 = 0.0;
    float ux2 = new_ux * new_ux;
    float uy2 = 0.0;
    float uxuy2 = 0.0;
    float u2 = ux2 + uy2;
    float u215 = 1.5 * u2;
    Cell *c = cell(x, y);
    c->n0 = four9ths * new_rho * (1 - u215);
    c->nE = one9th * new_rho * (1 + ux3 + 4.5 * ux2 - u215);
    c->nW = one9th * new_rho * (1 - ux3 + 4.5 * ux2 - u215);
    c->nN = one9th * new_rho * (1 + uy3 + 4.5 * uy2 - u215);
    c->nS = one9th * new_rho * (1 - uy3 + 4.5 * uy2 - u215);
    c->nNE = one36th * new_rho * (1 + ux3 + uy3 + 4.5 * (u2 + uxuy2) - u215);
    c->nSE = one36th * new_rho * (1 + ux3 - uy3 + 4.5 * (u2 - uxuy2) - u215);
    c->nNW = one36th * new_rho * (1 - ux3 + uy3 + 4.5 * (u2 - uxuy2) - u215);
    c->nSW = one36th * new_rho * (1 - ux3 - uy3 + 4.5 * (u2 + uxuy2) - u215);
    c->rho = new_rho;
    c->ux = new_ux;
    c->uy = 0.0;
}

// Initialize or re-initialize the fluid, based on speed slider setting:
void InitCells() {
    for (int y = 0; y < Y_DIM; y++) {
        for (int x = 0; x < X_DIM; x++) {
            SetEquilibrium(x, y);
        }
    }

    // Create a simple linear "wall" barrier (intentionally a little offset from center).
    int barrier_size = 8;
    for (int y = (Y_DIM / 2) - barrier_size; y <= (Y_DIM / 2) + barrier_size; y++) {
        int x = Y_DIM / 3.0;
        cell(x, y)->barrier = true;
    }
}

// Collide particles within each cell (here's the physics!).
void Collide() {
    float viscosity = 0.02;	// kinematic viscosity coefficient in natural units
    float omega = 1.0 / (3.0 * viscosity + 0.5);		// reciprocal of relaxation time
    for (int y = 1; y < Y_DIM - 1; y++) {
        for (int x = 1; x < X_DIM - 1; x++) {
            Cell *c = cell(x, y);
            float this_rho = c->n0 + c->nN + c->nS + c->nE + c->nW + c->nNW + c->nNE + c->nSW + c->nSE;
            c->rho = this_rho;
            float this_ux = (c->nE + c->nNE + c->nSE - c->nW - c->nNW - c->nSW) / this_rho;
            c->ux = this_ux;
            float this_uy = (c->nN + c->nNE + c->nNW - c->nS - c->nSE - c->nSW) / this_rho;
            c->uy = this_uy;
            float one9th_rho = one9th * this_rho;		// pre-compute a bunch of stuff for optimization
            float one36th_rho = one36th * this_rho;
            float ux3 = 3 * this_ux;
            float uy3 = 3 * this_uy;
            float ux2 = this_ux * this_ux;
            float uy2 = this_uy * this_uy;
            float uxuy2 = 2 * this_ux * this_uy;
            float u2 = ux2 + uy2;
            float u215 = 1.5 * u2;
            c->n0 += omega * (four9ths * this_rho * (1 - u215) - c->n0);
            c->nE += omega * (one9th_rho * (1 + ux3 + 4.5*ux2 - u215) - c->nE);
            c->nW += omega * (one9th_rho * (1 - ux3 + 4.5*ux2 - u215) - c->nW);
            c->nN += omega * (one9th_rho * (1 + uy3 + 4.5*uy2 - u215) - c->nN);
            c->nS += omega * (one9th_rho * (1 - uy3 + 4.5*uy2 - u215) - c->nS);
            c->nNE += omega * (one36th_rho * (1 + ux3 + uy3 + 4.5*(u2 + uxuy2) - u215) - c->nNE);
            c->nSE += omega * (one36th_rho * (1 + ux3 - uy3 + 4.5*(u2 - uxuy2) - u215) - c->nSE);
            c->nNW += omega * (one36th_rho * (1 - ux3 + uy3 + 4.5*(u2 - uxuy2) - u215) - c->nNW);
            c->nSW += omega * (one36th_rho * (1 - ux3 - uy3 + 4.5*(u2 + uxuy2) - u215) - c->nSW);
        }
    }

    for (int y = 1; y < Y_DIM - 2; y++) {
        cell(X_DIM - 1, y)->nW = cell(X_DIM - 2, y)->nW;    // at right end, copy left-flowing densities from next row to the left
        cell(X_DIM - 1, y)->nNW = cell(X_DIM - 2, y)->nNW;
        cell(X_DIM - 1, y)->nSW = cell(X_DIM - 2, y)->nSW;
    }
}

// Move particles along their directions of motion.
void Stream() {
    for (int y = Y_DIM - 2; y > 0; y--) {			// first start in NW corner...
        for (int x = 1; x < X_DIM - 1; x++) {
            cell(x, y)->nN = cell(x, y - 1)->nN;			// move the north-moving particles
            cell(x, y)->nNW = cell(x + 1, y - 1)->nNW;		// and the northwest-moving particles
        }
    }
    for (int y = Y_DIM - 2; y > 0; y--) {			// now start in NE corner...
        for (int x = X_DIM - 2; x > 0; x--) {
            cell(x, y)->nE = cell(x - 1, y)->nE;			// move the east-moving particles
            cell(x, y)->nNE = cell(x - 1 , y - 1)->nNE;		// and the northeast-moving particles
        }
    }
    for (int y = 1; y < Y_DIM - 1; y++) {			// now start in SE corner...
        for (int x = X_DIM - 2; x > 0; x--) {
            cell(x, y)->nS = cell(x, y + 1)->nS;			// move the south-moving particles
            cell(x, y)->nSE = cell(x - 1, y + 1)->nSE;		// and the southeast-moving particles
        }
    }
    for (int y = 1; y < Y_DIM - 1; y++) {				// now start in the SW corner...
        for (int x = 1; x < X_DIM - 1; x++) {
            cell(x, y)->nW = cell(x + 1, y)->nW;			// move the west-moving particles
            cell(x, y)->nSW = cell(x + 1, y + 1)->nSW;		// and the southwest-moving particles
        }
    }
    for (int y = 1; y < Y_DIM - 1; y++) {				// Now handle bounce-back from barriers
        for (int x = 1; x < X_DIM - 1; x++) {
            if (cell(x, y)->barrier) {
                cell(x + 1, y)->nE = cell(x, y)->nW;
                cell(x - 1, y)->nW = cell(x, y)->nE;
                cell(x, y + 1)->nN = cell(x, y)->nS;
                cell(x, y - 1)->nS = cell(x, y)->nN;
                cell(x + 1, y + 1)->nNE = cell(x, y)->nSW;
                cell(x - 1, y + 1)->nNW = cell(x, y)->nSE;
                cell(x + 1, y - 1)->nSE = cell(x, y)->nNW;
                cell(x - 1, y - 1)->nSW = cell(x, y)->nNE;
            }
        }
    }
}

void InitTracers() {
    for (int i = 0; i < NUM_TRACERS; i++) {
        tracers_x[i] = X_DIM / (float)NUM_TRACERS * i;
        tracers_y[i] = (float)rand() / (float)RAND_MAX * Y_DIM;
    }
}

void MoveTracers() {
    for (int i = 0; i < NUM_TRACERS; i++) {
        Cell *c = cell(tracers_x[i], tracers_y[i]);
        tracers_x[i] += c->ux;
        tracers_y[i] += c->uy;
        if (tracers_x[i] > X_DIM - 1) {
            tracers_x[i] = 0.0;
            tracers_y[i] = (float)rand() / (float)RAND_MAX * Y_DIM;
        }
    }
}

void DrawTracers() {
    for (int i = 0; i < NUM_TRACERS; i++) {
        RectFill(g_window->bmp, tracers_x[i] * RENDER_SCALE, tracers_y[i] * RENDER_SCALE,
            RENDER_SCALE, RENDER_SCALE, g_colourBlack);
    }
}

void Draw() {
    BitmapClear(g_window->bmp, g_colourWhite);

    for (int y = 1; y < Y_DIM - 1; y++) {
        for (int x = 1; x < X_DIM - 1; x++) {
            DfColour col = g_colourBlack;

            if (!cell(x, y)->barrier) {
                // Compute the curl (actually times 2) of the macroscopic velocity field.
                float curl = cell(x + 1, y)->uy -
                    cell(x - 1, y)->uy -
                    cell(x, y + 1)->ux +
                    cell(x, y - 1)->ux;

                float contrast = 2000.0;
                col = g_colourWhite;
                int tmp = ClampInt(abs(curl * contrast), 0, 255);
                if (curl < 0.0) {
                    col.g = 255 - tmp;
                    col.r = col.g;
                }
                else {
                    col.g = 255 - tmp;
                    col.b = col.g;
                }
            }

            RectFill(g_window->bmp, x * RENDER_SCALE, y * RENDER_SCALE,
                RENDER_SCALE, RENDER_SCALE, col);
        }
    }

    DrawTracers();
}

void Simulate() {
    int steps_per_frame = 10;
    for (int step = 0; step < steps_per_frame; step++) {
        Collide();
        Stream();
        MoveTracers();
    }
}

void LatticeBoltzmannMain()
{
    CreateWin(X_DIM * RENDER_SCALE, Y_DIM * RENDER_SCALE, WT_WINDOWED,
        "Lattice Boltzmann Method Fluid Simulation Example");
    g_defaultFont = LoadFontFromMemory(deadfrog_mono_7x13, sizeof(deadfrog_mono_7x13));

    InitCells();
    InitTracers();

    while (!g_window->windowClosed && !g_input.keys[KEY_ESC]) {
        InputPoll();

        Simulate();
        Draw();

        // Draw frames per second counter
        RectFill(g_window->bmp, g_window->bmp->width - 55, 0, 55, g_defaultFont->charHeight + 2, g_colourBlack);
        DrawTextLeft(g_defaultFont, g_colourWhite, g_window->bmp, g_window->bmp->width - 50, 0, "FPS:%i", g_window->fps);

        UpdateWin();
        WaitVsync();
    }
}
<commit_msg>LBM example - Don't bother storing rho (density) any more. It isn't used by anything, and even if it was, it can be calculated from the nN, nNE, nE etc. Various other tidy-ups too.<commit_after>#include "df_font.h"
#include "df_window.h"
#include "fonts/df_mono.h"

#include <math.h>
#include <stdlib.h>

// Based on https://physics.weber.edu/schroeder/fluids/

struct Cell {
    float n0;
    float nN, nS, nE, nW;
    float nNE, nSE, nNW, nSW;
    float ux, uy;
    bool barrier;
};

enum { X_DIM = 400 };
enum { Y_DIM = 160 };
enum { RENDER_SCALE = 3 }; // Width of cell in pixels.
enum { NUM_TRACERS = 144 };
static const float fluid_speed = 0.1f;
static const float viscosity = 0.02f;
static const float four9ths = 4.0 / 9.0;					// abbreviations
static const float one9th = 1.0 / 9.0;
static const float one36th = 1.0 / 36.0;

// Index into this 2D array using x + y*X_DIM, traversing rows first and then columns.
static Cell g_cells[X_DIM * Y_DIM];
float g_tracers_x[NUM_TRACERS];
float g_tracers_y[NUM_TRACERS];

static Cell *cell(int x, int y) {
    return &g_cells[x + y * X_DIM];
}

// Set all densities in a cell to their equilibrium values.
void SetEquilibrium(int x, int y) {
    float new_ux = fluid_speed;
    float ux3 = 3 * new_ux;
    float uy3 = 0.0;
    float ux2 = new_ux * new_ux;
    float uy2 = 0.0;
    float uxuy2 = 0.0;
    float u2 = ux2 + uy2;
    float u215 = 1.5 * u2;
    Cell *c = cell(x, y);
    c->n0 = four9ths * (1 - u215);
    c->nE = one9th * (1 + ux3 + 4.5 * ux2 - u215);
    c->nW = one9th * (1 - ux3 + 4.5 * ux2 - u215);
    c->nN = one9th * (1 + uy3 + 4.5 * uy2 - u215);
    c->nS = one9th * (1 - uy3 + 4.5 * uy2 - u215);
    c->nNE = one36th * (1 + ux3 + uy3 + 4.5 * (u2 + uxuy2) - u215);
    c->nSE = one36th * (1 + ux3 - uy3 + 4.5 * (u2 - uxuy2) - u215);
    c->nNW = one36th * (1 - ux3 + uy3 + 4.5 * (u2 - uxuy2) - u215);
    c->nSW = one36th * (1 - ux3 - uy3 + 4.5 * (u2 + uxuy2) - u215);
    c->ux = new_ux;
    c->uy = 0.0;
}

// Initialize or re-initialize the fluid, based on speed slider setting:
void InitCells() {
    for (int y = 0; y < Y_DIM; y++) {
        for (int x = 0; x < X_DIM; x++) {
            SetEquilibrium(x, y);
        }
    }

    // Create a simple linear "wall" barrier (intentionally a little offset from center).
    int barrier_size = 8;
    for (int y = (Y_DIM / 2) - barrier_size; y <= (Y_DIM / 2) + barrier_size; y++) {
        int x = Y_DIM / 3.0;
        cell(x, y)->barrier = true;
    }
}

// Collide particles within each cell (here's the physics!).
void Collide() {
    float viscosity = 0.02;	// kinematic viscosity coefficient in natural units
    float omega = 1.0 / (3.0 * viscosity + 0.5);		// reciprocal of relaxation time
    for (int y = 1; y < Y_DIM - 1; y++) {
        for (int x = 1; x < X_DIM - 1; x++) {
            Cell *c = cell(x, y);
            float this_rho = c->n0 + c->nN + c->nS + c->nE + c->nW + c->nNW + c->nNE + c->nSW + c->nSE;
            float this_ux = (c->nE + c->nNE + c->nSE - c->nW - c->nNW - c->nSW) / this_rho;
            c->ux = this_ux;
            float this_uy = (c->nN + c->nNE + c->nNW - c->nS - c->nSE - c->nSW) / this_rho;
            c->uy = this_uy;
            float one9th_rho = one9th * this_rho;		// pre-compute a bunch of stuff for optimization
            float one36th_rho = one36th * this_rho;
            float ux3 = 3 * this_ux;
            float uy3 = 3 * this_uy;
            float ux2 = this_ux * this_ux;
            float uy2 = this_uy * this_uy;
            float uxuy2 = 2 * this_ux * this_uy;
            float u2 = ux2 + uy2;
            float u215 = 1.5 * u2;
            c->n0 += omega * (four9ths * this_rho * (1 - u215) - c->n0);
            c->nE += omega * (one9th_rho * (1 + ux3 + 4.5*ux2 - u215) - c->nE);
            c->nW += omega * (one9th_rho * (1 - ux3 + 4.5*ux2 - u215) - c->nW);
            c->nN += omega * (one9th_rho * (1 + uy3 + 4.5*uy2 - u215) - c->nN);
            c->nS += omega * (one9th_rho * (1 - uy3 + 4.5*uy2 - u215) - c->nS);
            c->nNE += omega * (one36th_rho * (1 + ux3 + uy3 + 4.5*(u2 + uxuy2) - u215) - c->nNE);
            c->nSE += omega * (one36th_rho * (1 + ux3 - uy3 + 4.5*(u2 - uxuy2) - u215) - c->nSE);
            c->nNW += omega * (one36th_rho * (1 - ux3 + uy3 + 4.5*(u2 - uxuy2) - u215) - c->nNW);
            c->nSW += omega * (one36th_rho * (1 - ux3 - uy3 + 4.5*(u2 + uxuy2) - u215) - c->nSW);
        }
    }

    for (int y = 1; y < Y_DIM - 2; y++) {
        cell(X_DIM - 1, y)->nW = cell(X_DIM - 2, y)->nW;    // at right end, copy left-flowing densities from next row to the left
        cell(X_DIM - 1, y)->nNW = cell(X_DIM - 2, y)->nNW;
        cell(X_DIM - 1, y)->nSW = cell(X_DIM - 2, y)->nSW;
    }
}

// Move particles along their directions of motion.
void Stream() {
    for (int y = Y_DIM - 2; y > 0; y--) {			// first start in NW corner...
        for (int x = 1; x < X_DIM - 1; x++) {
            cell(x, y)->nN = cell(x, y - 1)->nN;			// move the north-moving particles
            cell(x, y)->nNW = cell(x + 1, y - 1)->nNW;		// and the northwest-moving particles
        }
    }
    for (int y = Y_DIM - 2; y > 0; y--) {			// now start in NE corner...
        for (int x = X_DIM - 2; x > 0; x--) {
            cell(x, y)->nE = cell(x - 1, y)->nE;			// move the east-moving particles
            cell(x, y)->nNE = cell(x - 1 , y - 1)->nNE;		// and the northeast-moving particles
        }
    }
    for (int y = 1; y < Y_DIM - 1; y++) {			// now start in SE corner...
        for (int x = X_DIM - 2; x > 0; x--) {
            cell(x, y)->nS = cell(x, y + 1)->nS;			// move the south-moving particles
            cell(x, y)->nSE = cell(x - 1, y + 1)->nSE;		// and the southeast-moving particles
        }
    }
    for (int y = 1; y < Y_DIM - 1; y++) {				// now start in the SW corner...
        for (int x = 1; x < X_DIM - 1; x++) {
            cell(x, y)->nW = cell(x + 1, y)->nW;			// move the west-moving particles
            cell(x, y)->nSW = cell(x + 1, y + 1)->nSW;		// and the southwest-moving particles
        }
    }
    for (int y = 1; y < Y_DIM - 1; y++) {				// Now handle bounce-back from barriers
        for (int x = 1; x < X_DIM - 1; x++) {
            if (cell(x, y)->barrier) {
                cell(x + 1, y)->nE = cell(x, y)->nW;
                cell(x - 1, y)->nW = cell(x, y)->nE;
                cell(x, y + 1)->nN = cell(x, y)->nS;
                cell(x, y - 1)->nS = cell(x, y)->nN;
                cell(x + 1, y + 1)->nNE = cell(x, y)->nSW;
                cell(x - 1, y + 1)->nNW = cell(x, y)->nSE;
                cell(x + 1, y - 1)->nSE = cell(x, y)->nNW;
                cell(x - 1, y - 1)->nSW = cell(x, y)->nNE;
            }
        }
    }
}

void InitTracers() {
    for (int i = 0; i < NUM_TRACERS; i++) {
        g_tracers_x[i] = X_DIM / (float)NUM_TRACERS * i;
        g_tracers_y[i] = (float)rand() / (float)RAND_MAX * Y_DIM;
    }
}

void MoveTracers() {
    for (int i = 0; i < NUM_TRACERS; i++) {
        Cell *c = cell(g_tracers_x[i], g_tracers_y[i]);
        g_tracers_x[i] += c->ux;
        g_tracers_y[i] += c->uy;
        if (g_tracers_x[i] > X_DIM - 1) {
            g_tracers_x[i] = 0.0;
            g_tracers_y[i] = (float)rand() / (float)RAND_MAX * Y_DIM;
        }
    }
}

void DrawTracers() {
    for (int i = 0; i < NUM_TRACERS; i++) {
        RectFill(g_window->bmp, g_tracers_x[i] * RENDER_SCALE, g_tracers_y[i] * RENDER_SCALE,
            RENDER_SCALE, RENDER_SCALE, g_colourBlack);
    }
}

void Draw() {
    BitmapClear(g_window->bmp, g_colourWhite);

    for (int y = 1; y < Y_DIM - 1; y++) {
        for (int x = 1; x < X_DIM - 1; x++) {
            DfColour col = g_colourBlack;

            if (!cell(x, y)->barrier) {
                // Compute the curl (actually times 2) of the macroscopic velocity field.
                float curl = cell(x + 1, y)->uy -
                    cell(x - 1, y)->uy -
                    cell(x, y + 1)->ux +
                    cell(x, y - 1)->ux;

                float contrast = 2000.0;
                col = g_colourWhite;
                int tmp = ClampInt(abs(curl * contrast), 0, 255);
                if (curl < 0.0) {
                    col.g = 255 - tmp;
                    col.r = col.g;
                }
                else {
                    col.g = 255 - tmp;
                    col.b = col.g;
                }
            }

            RectFill(g_window->bmp, x * RENDER_SCALE, y * RENDER_SCALE,
                RENDER_SCALE, RENDER_SCALE, col);
        }
    }

    DrawTracers();
}

void Simulate() {
    for (int step = 0; step < 10; step++) {
        Collide();
        Stream();
        MoveTracers();
    }
}

void LatticeBoltzmannMain() {
    CreateWin(X_DIM * RENDER_SCALE, Y_DIM * RENDER_SCALE, WT_WINDOWED,
        "Lattice Boltzmann Method Fluid Simulation Example");
    g_defaultFont = LoadFontFromMemory(deadfrog_mono_7x13, sizeof(deadfrog_mono_7x13));

    InitCells();
    InitTracers();

    while (!g_window->windowClosed && !g_input.keys[KEY_ESC]) {
        InputPoll();

        Simulate();
        Draw();

        // Draw frames per second counter
        RectFill(g_window->bmp, g_window->bmp->width - 55, 0, 55, g_defaultFont->charHeight + 2, g_colourBlack);
        DrawTextLeft(g_defaultFont, g_colourWhite, g_window->bmp, g_window->bmp->width - 50, 0, "FPS:%i", g_window->fps);

        UpdateWin();
        WaitVsync();
    }
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
///
/// \file main.cpp
/// \brief Main file for testing different system classes
///        and functionality.
/// Author: Andrew Watson
/// Created: 1/22/13
/// Email: watsontandrew@gmail.com
///
////////////////////////////////////////////////////////////////////////////////
#include <hokuyo.hpp>
#include <time.h>
#include <wiringPi.h>

//#define TESTHOKUYO
#ifdef TESTHOKUYO
class ExampleLaserCallback : public Laser::Callback
{
public:
    ExampleLaserCallback(){}
    ~ExampleLaserCallback(){}
    virtual void ProcessLaserData(const std::vector<CvPoint3D64f>& scan,
                                  const time_t& timestamp)
    {
        mLaserScan = scan;
        mTimeStamp = timestamp;
    }
    std::vector<CvPoint3D64f> mLaserScan;
    time_t mTimeStamp;
};

double MiddleScanDistanceInches (const std::vector<CvPoint3D64f>& scan)
{
    if(scan.size() > 0)
        return scan.at(scan.size() / 2.).x * 1000. / 25.4;
    else
        return 0;
}

int main()
{
    Laser::Hokuyo* laser = new Laser::Hokuyo();
    ExampleLaserCallback callback;
    laser->RegisterCallback(&callback);
    if(laser->Initialize())
    {
        if(laser->StartCaptureThread())
        {
            while (1)
            {
                std::cout << MiddleScanDistanceInches(callback.mLaserScan)
                          << std::endl;
                boost::this_thread::sleep(boost::posix_time::millisec(10));
            }
            laser->Shutdown();
            delete laser;
        }
    }

    return 0;
}
#endif

//#define TESTDYNAMIXEL
#ifdef TESTDYNAMIXEL
int main()
{
    int x = 1234;

    char bytes[sizeof x];
    std::copy(static_cast<const char*>(static_cast<const void*>(&x)),
              static_cast<const char*>(static_cast<const void*>(&x)) + sizeof x,
              bytes);

    char a = bytes[0];
    char b = bytes[1];

   return 0;
}
#endif

//#define TESTTIMING
#ifdef TESTTIMING
timespec diff(timespec start, timespec end);
int main()
{
//    clockid_t types[] = {CLOCK_REALTIME, CLOCK_MONOTONIC,
//                         CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID,
//                         (clockid_t) - 1};
//    struct timespec spec;
//    for (int i = 0; types[i] != (clockid_t) - 1; i++)
//    {
//        if(clock_getres(types[i], &spec) != 0)
//        {
//            std::cout << "Timer " << types[i] << " not supported" << std::endl;
//        }
//        else
//        {
//            std::cout << "Timer: " << i << " Seconds: " << spec.tv_sec
//                      << " Nanos: " << spec.tv_nsec << std::endl;
//        }
//    }
    timespec t1, t2;
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t1);
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t2);
    while(1)
    {
        if(diff(t1, t2).tv_nsec > 2000000)
        {
            std::cout << "1 Sec Elapsed" << std::endl;
            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t1);
            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t2);
        }
        else
        {
            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t2);
        }
        std::cout << diff(t1, t2).tv_nsec << std::endl;
        nanosleep((struct timespec[]){{0, 100000000}}, NULL);
    }
    return 0;
}
timespec diff(timespec start, timespec end)
{
    timespec temp;
    if((end.tv_nsec-start.tv_nsec)<0)
    {
        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
    }
    else
    {
        temp.tv_sec = end.tv_sec - start.tv_sec;
        temp.tv_nsec = end.tv_nsec - start.tv_nsec;
    }
    return temp;
}

#endif

//#define TESTSERVER
#ifdef TESTSERVER
int main()
{
    return 0;
}

#endif

#define TESTISR
#ifdef TESTISR

#define BUTTON_PIN 0
volatile int eventCounter = 0;
void myInterrupt(void) {eventCounter++;}

int main()
{

    if (wiringPiSetup () < 0) {
          fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno));
          return 1;
      }

      // set Pin 17/0 generate an interrupt on high-to-low transitions
      // and attach myInterrupt() to the interrupt
      if ( wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0 ) {
          fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno));
          return 1;
      }

      // display counter value every second.
      while ( 1 ) {
        printf( "%d\n", eventCounter );
        eventCounter = 0;
        delay( 1000 ); // wait 1 second
      }

      return 0;
}

#endif
/** End of File */
<commit_msg>Added TCP Webserver using Boost ASIO<commit_after>////////////////////////////////////////////////////////////////////////////////
///
/// \file main.cpp
/// \brief Main file for testing different system classes
///        and functionality.
/// Author: Andrew Watson
/// Created: 1/22/13
/// Email: watsontandrew@gmail.com
///
////////////////////////////////////////////////////////////////////////////////
#include <hokuyo.hpp>
#include <time.h>
#include <wiringPi.h> //wiringpi testing

//Webserver testing
#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
//end webserver testing

//#define TESTHOKUYO
#ifdef TESTHOKUYO
class ExampleLaserCallback : public Laser::Callback
{
public:
    ExampleLaserCallback(){}
    ~ExampleLaserCallback(){}
    virtual void ProcessLaserData(const std::vector<CvPoint3D64f>& scan,
                                  const time_t& timestamp)
    {
        mLaserScan = scan;
        mTimeStamp = timestamp;
    }
    std::vector<CvPoint3D64f> mLaserScan;
    time_t mTimeStamp;
};

double MiddleScanDistanceInches (const std::vector<CvPoint3D64f>& scan)
{
    if(scan.size() > 0)
        return scan.at(scan.size() / 2.).x * 1000. / 25.4;
    else
        return 0;
}

int main()
{
    Laser::Hokuyo* laser = new Laser::Hokuyo();
    ExampleLaserCallback callback;
    laser->RegisterCallback(&callback);
    if(laser->Initialize())
    {
        if(laser->StartCaptureThread())
        {
            while (1)
            {
                std::cout << MiddleScanDistanceInches(callback.mLaserScan)
                          << std::endl;
                boost::this_thread::sleep(boost::posix_time::millisec(10));
            }
            laser->Shutdown();
            delete laser;
        }
    }

    return 0;
}
#endif

//#define TESTDYNAMIXEL
#ifdef TESTDYNAMIXEL
int main()
{
    int x = 1234;

    char bytes[sizeof x];
    std::copy(static_cast<const char*>(static_cast<const void*>(&x)),
              static_cast<const char*>(static_cast<const void*>(&x)) + sizeof x,
              bytes);

    char a = bytes[0];
    char b = bytes[1];

   return 0;
}
#endif

//#define TESTTIMING
#ifdef TESTTIMING
timespec diff(timespec start, timespec end);
int main()
{
//    clockid_t types[] = {CLOCK_REALTIME, CLOCK_MONOTONIC,
//                         CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID,
//                         (clockid_t) - 1};
//    struct timespec spec;
//    for (int i = 0; types[i] != (clockid_t) - 1; i++)
//    {
//        if(clock_getres(types[i], &spec) != 0)
//        {
//            std::cout << "Timer " << types[i] << " not supported" << std::endl;
//        }
//        else
//        {
//            std::cout << "Timer: " << i << " Seconds: " << spec.tv_sec
//                      << " Nanos: " << spec.tv_nsec << std::endl;
//        }
//    }
    timespec t1, t2;
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t1);
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t2);
    while(1)
    {
        if(diff(t1, t2).tv_nsec > 2000000)
        {
            std::cout << "1 Sec Elapsed" << std::endl;
            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t1);
            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t2);
        }
        else
        {
            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t2);
        }
        std::cout << diff(t1, t2).tv_nsec << std::endl;
        nanosleep((struct timespec[]){{0, 100000000}}, NULL);
    }
    return 0;
}
timespec diff(timespec start, timespec end)
{
    timespec temp;
    if((end.tv_nsec-start.tv_nsec)<0)
    {
        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
    }
    else
    {
        temp.tv_sec = end.tv_sec - start.tv_sec;
        temp.tv_nsec = end.tv_nsec - start.tv_nsec;
    }
    return temp;
}

#endif

#define TESTSERVER
#ifdef TESTSERVER
//Requires SUDO to run in order to bind to port
using boost::asio::ip::tcp;
std::string make_daytime_string()
{
  using namespace std; // For time_t, time and ctime;
  time_t now = time(0);
  return ctime(&now);
}

int main()
{
  try
  {
    boost::asio::io_service io_service;

    tcp::endpoint endpoint(tcp::v4(), 13);
    tcp::acceptor acceptor(io_service, endpoint);

    for (;;)
    {
      tcp::iostream stream;
      acceptor.accept(*stream.rdbuf());
      stream << make_daytime_string();
    }
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

#endif

//#define TESTISR
#ifdef TESTISR

#define BUTTON_PIN 0
volatile int eventCounter = 0;
void myInterrupt(void) {eventCounter++;}

int main()
{

    if (wiringPiSetup () < 0) {
          fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno));
          return 1;
      }

      // set Pin 17/0 generate an interrupt on high-to-low transitions
      // and attach myInterrupt() to the interrupt
      if ( wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0 ) {
          fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno));
          return 1;
      }

      // display counter value every second.
      while ( 1 ) {
        printf( "%d\n", eventCounter );
        eventCounter = 0;
        delay( 1000 ); // wait 1 second
      }

      return 0;
}

#endif
/** End of File */
<|endoftext|>


<commit_before>// -*-c++-*-

#include <osgProducer/Viewer>

#include <osgDB/ReadFile>

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/StateSet>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TextureRectangle>
#include <osg/TexMat>
#include <osg/CullFace>
#include <osg/ImageStream>

#include <osgGA/TrackballManipulator>

osg::ImageStream* s_imageStream = 0;
class PostSwapFinishCallback : public Producer::Camera::Callback
{
public:

    PostSwapFinishCallback() {}

    virtual void operator()(const Producer::Camera& camera)
    {
        // osg::Timer_t start_tick = osg::Timer::instance()->tick();

        osgProducer::OsgSceneHandler* sh = const_cast<osgProducer::OsgSceneHandler*>(dynamic_cast<const osgProducer::OsgSceneHandler*>(camera.getSceneHandler()));

        if (s_imageStream && s_imageStream->getPixelBufferObject()) s_imageStream->getPixelBufferObject()->compileBuffer(*(sh->getSceneView()->getState()));
        // glFinish();

        //osg::notify(osg::NOTICE)<<"callback after PBO "<<osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<<std::endl;
    }
};

class MovieEventHandler : public osgGA::GUIEventHandler
{
public:

    MovieEventHandler() {}

    void set(osg::Node* node);

    virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); }

    virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);

    virtual void getUsage(osg::ApplicationUsage& usage) const;

    typedef std::vector< osg::ref_ptr<osg::ImageStream> > ImageStreamList;

protected:

    virtual ~MovieEventHandler() {}

    class FindImageStreamsVisitor : public osg::NodeVisitor
    {
    public:
        FindImageStreamsVisitor(ImageStreamList& imageStreamList):
            _imageStreamList(imageStreamList) {}

        virtual void apply(osg::Geode& geode)
        {
            apply(geode.getStateSet());

            for(unsigned int i=0;i<geode.getNumDrawables();++i)
            {
                apply(geode.getDrawable(i)->getStateSet());
            }

            traverse(geode);
        }

        virtual void apply(osg::Node& node)
        {
            apply(node.getStateSet());
            traverse(node);
        }

        inline void apply(osg::StateSet* stateset)
        {
            if (!stateset) return;

            osg::StateAttribute* attr = stateset->getTextureAttribute(0,osg::StateAttribute::TEXTURE);
            if (attr)
            {
                osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(attr);
                if (texture2D) apply(dynamic_cast<osg::ImageStream*>(texture2D->getImage()));

                osg::TextureRectangle* textureRec = dynamic_cast<osg::TextureRectangle*>(attr);
                if (textureRec) apply(dynamic_cast<osg::ImageStream*>(textureRec->getImage()));
            }
        }

        inline void apply(osg::ImageStream* imagestream)
        {
            if (imagestream)
            {
                _imageStreamList.push_back(imagestream);
                s_imageStream = imagestream;
            }
        }

        ImageStreamList& _imageStreamList;
    };

    ImageStreamList _imageStreamList;

};

void MovieEventHandler::set(osg::Node* node)
{
    _imageStreamList.clear();
    if (node)
    {
        FindImageStreamsVisitor fisv(_imageStreamList);
        node->accept(fisv);
    }
}

bool MovieEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&)
{
    switch(ea.getEventType())
    {
        case(osgGA::GUIEventAdapter::KEYDOWN):
        {
            if (ea.getKey()=='s')
            {
                for(ImageStreamList::iterator itr=_imageStreamList.begin();
                    itr!=_imageStreamList.end();
                    ++itr)
                {
                    std::cout<<"Play"<<std::endl;
                     (*itr)->play();
                }
                return true;
            }
            else if (ea.getKey()=='p')
            {
                for(ImageStreamList::iterator itr=_imageStreamList.begin();
                    itr!=_imageStreamList.end();
                    ++itr)
                {
                    std::cout<<"Pause"<<std::endl;
                    (*itr)->pause();
                }
                return true;
            }
            else if (ea.getKey()=='r')
            {
                return true;
            }
            else if (ea.getKey()=='l')
            {
                return true;
            }
            return false;
        }

        default:
            return false;
    }
}

void MovieEventHandler::getUsage(osg::ApplicationUsage& usage) const
{
    usage.addKeyboardMouseBinding("p","Pause movie");
    usage.addKeyboardMouseBinding("s","Play movie");
    usage.addKeyboardMouseBinding("r","Start movie");
    usage.addKeyboardMouseBinding("l","Toggle looping of movie");
}

osg::Geometry* createTexturedQuadGeometry(const osg::Vec3& pos,float width,float height, osg::Image* image, bool useTextureRectangle)
{
    if (useTextureRectangle)
    {
        osg::Geometry* pictureQuad = createTexturedQuadGeometry(pos,
                                           osg::Vec3(width,0.0f,0.0f),
                                           osg::Vec3(0.0f,0.0f,height),
                                           0.0f,image->t(), image->s(),0.0f);

        pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0,
                    new osg::TextureRectangle(image),
                    osg::StateAttribute::ON);

        return pictureQuad;
    }
    else
    {
        osg::Geometry* pictureQuad = createTexturedQuadGeometry(pos,
                                           osg::Vec3(width,0.0f,0.0f),
                                           osg::Vec3(0.0f,0.0f,height),
                                           0.0f,1.0f, 1.0f,0.0f);

        osg::Texture2D* texture = new osg::Texture2D(image);
        texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);

        pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0,
                    texture,
                    osg::StateAttribute::ON);

        return pictureQuad;
    }
}

int main(int argc, char** argv)
{
    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    // set up the usage document, in case we need to print out how to use this program.
    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models.");
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");

    bool useTextureRectangle = true;
    bool useShader = false;

    // construct the viewer.
    osgProducer::Viewer viewer(arguments);

    while (arguments.read("--texture2D")) useTextureRectangle=false;
    while (arguments.read("--shader")) useShader=true;

    // set up the value with sensible default event handlers.
    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);

    // register the handler to add keyboard and mosue handling.
    MovieEventHandler* meh = new MovieEventHandler();
    viewer.getEventHandlerList().push_front(meh);

    // get details on keyboard and mouse bindings used by the viewer.
    viewer.getUsage(*arguments.getApplicationUsage());

    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    {
        arguments.getApplicationUsage()->write(std::cout);
        return 1;
    }

    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    osg::Vec3 pos(0.0f,0.0f,0.0f);

    osg::StateSet* stateset = geode->getOrCreateStateSet();
    stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);

    if (useShader)
    {
        //useTextureRectangle = false;

        static const char *shaderSourceTextureRec = {
            "uniform vec4 cutoff_color;\n"
            "uniform samplerRect movie_texture;\n"
            "void main(void)\n"
            "{\n"
            "    vec4 texture_color = textureRect(movie_texture, gl_TexCoord[0]); \n"
            "    if (all(lessThanEqual(texture_color,cutoff_color))) discard; \n"
            "    gl_FragColor = texture_color;\n"
            "}\n"
        };

        static const char *shaderSourceTexture2D = {
            "uniform vec4 cutoff_color;\n"
            "uniform sampler2D movie_texture;\n"
            "void main(void)\n"
            "{\n"
            "    vec4 texture_color = texture2D(movie_texture, gl_TexCoord[0]); \n"
            "    if (all(lessThanEqual(texture_color,cutoff_color))) discard; \n"
            "    gl_FragColor = texture_color;\n"
            "}\n"
        };

        osg::Program* program = new osg::Program;

        program->addShader(new osg::Shader(osg::Shader::FRAGMENT,
                                           useTextureRectangle ? shaderSourceTextureRec : shaderSourceTexture2D));

        stateset->addUniform(new osg::Uniform("cutoff_color",osg::Vec4(0.1f,0.1f,0.1f,1.0f)));
        stateset->addUniform(new osg::Uniform("movie_texture",0));

        stateset->setAttribute(program);

    }

    for(int i=1;i<arguments.argc();++i)
    {
        if (arguments.isString(i))
        {
            osg::Image* image = osgDB::readImageFile(arguments[i]);
            osg::ImageStream* imagestream = dynamic_cast<osg::ImageStream*>(image);
            if (imagestream) imagestream->play();

            if (image)
            {
                geode->addDrawable(createTexturedQuadGeometry(pos,image->s(),image->t(),image, useTextureRectangle));

                pos.z() += image->t()*1.5f;
            }
            else
            {
                std::cout<<"Unable to read file "<<arguments[i]<<std::endl;
            }
        }
    }

    if (geode->getNumDrawables()==0)
    {
        // nothing loaded.
        return 1;
    }

    // pass the model to the MovieEventHandler so it can pick out ImageStream's to manipulate.
    meh->set(geode.get());

    // report any errors if they have occured when parsing the program aguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }

    if (arguments.argc()<=1)
    {
        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
        return 1;
    }

    // any option left unread are converted into errors to write out later.
    arguments.reportRemainingOptionsAsUnrecognized();

    // report any errors if they have occured when parsing the program aguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
    }
/*
    // set up a post swap callback to flush deleted GL objects and compile new GL objects
    for(unsigned int cameraNum=0;cameraNum<viewer.getNumberOfCameras();++cameraNum)
    {
        Producer::Camera* camera=viewer.getCamera(cameraNum);
        camera->addPostSwapCallback(new PostSwapFinishCallback());
    }
*/
    // set the scene to render
    viewer.setSceneData(geode.get());

    // create the windows and run the threads.
    viewer.realize();

    while( !viewer.done() )
    {
        // wait for all cull and draw threads to complete.
        viewer.sync();

        // update the scene by traversing it with the the update visitor which will
        // call all node update callbacks and animations.
        viewer.update();

        // fire off the cull and draw traversals of the scene.
        viewer.frame();

    }

    // wait for all cull and draw threads to complete before exit.
    viewer.sync();

    return 0;

}
<commit_msg>From Sondra Iverson, "The options to rewind/restart and to toggle looping in the osgmovie example have been implemented. "<commit_after>// -*-c++-*-

#include <osgProducer/Viewer>

#include <osgDB/ReadFile>

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/StateSet>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TextureRectangle>
#include <osg/TexMat>
#include <osg/CullFace>
#include <osg/ImageStream>

#include <osgGA/TrackballManipulator>

osg::ImageStream* s_imageStream = 0;
class PostSwapFinishCallback : public Producer::Camera::Callback
{
public:

    PostSwapFinishCallback() {}

    virtual void operator()(const Producer::Camera& camera)
    {
        // osg::Timer_t start_tick = osg::Timer::instance()->tick();

        osgProducer::OsgSceneHandler* sh = const_cast<osgProducer::OsgSceneHandler*>(dynamic_cast<const osgProducer::OsgSceneHandler*>(camera.getSceneHandler()));

        if (s_imageStream && s_imageStream->getPixelBufferObject()) s_imageStream->getPixelBufferObject()->compileBuffer(*(sh->getSceneView()->getState()));
        // glFinish();

        //osg::notify(osg::NOTICE)<<"callback after PBO "<<osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<<std::endl;
    }
};

class MovieEventHandler : public osgGA::GUIEventHandler
{
public:

    MovieEventHandler() {}

    void set(osg::Node* node);

    virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); }

    virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);

    virtual void getUsage(osg::ApplicationUsage& usage) const;

    typedef std::vector< osg::ref_ptr<osg::ImageStream> > ImageStreamList;

protected:

    virtual ~MovieEventHandler() {}

    class FindImageStreamsVisitor : public osg::NodeVisitor
    {
    public:
        FindImageStreamsVisitor(ImageStreamList& imageStreamList):
            _imageStreamList(imageStreamList) {}

        virtual void apply(osg::Geode& geode)
        {
            apply(geode.getStateSet());

            for(unsigned int i=0;i<geode.getNumDrawables();++i)
            {
                apply(geode.getDrawable(i)->getStateSet());
            }

            traverse(geode);
        }

        virtual void apply(osg::Node& node)
        {
            apply(node.getStateSet());
            traverse(node);
        }

        inline void apply(osg::StateSet* stateset)
        {
            if (!stateset) return;

            osg::StateAttribute* attr = stateset->getTextureAttribute(0,osg::StateAttribute::TEXTURE);
            if (attr)
            {
                osg::Texture2D* texture2D = dynamic_cast<osg::Texture2D*>(attr);
                if (texture2D) apply(dynamic_cast<osg::ImageStream*>(texture2D->getImage()));

                osg::TextureRectangle* textureRec = dynamic_cast<osg::TextureRectangle*>(attr);
                if (textureRec) apply(dynamic_cast<osg::ImageStream*>(textureRec->getImage()));
            }
        }

        inline void apply(osg::ImageStream* imagestream)
        {
            if (imagestream)
            {
                _imageStreamList.push_back(imagestream);
                s_imageStream = imagestream;
            }
        }

        ImageStreamList& _imageStreamList;
    };

    ImageStreamList _imageStreamList;

};

void MovieEventHandler::set(osg::Node* node)
{
    _imageStreamList.clear();
    if (node)
    {
        FindImageStreamsVisitor fisv(_imageStreamList);
        node->accept(fisv);
    }
}

bool MovieEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&)
{
    switch(ea.getEventType())
    {
        case(osgGA::GUIEventAdapter::KEYDOWN):
        {
            if (ea.getKey()=='s')
            {
                for(ImageStreamList::iterator itr=_imageStreamList.begin();
                    itr!=_imageStreamList.end();
                    ++itr)
                {
                    std::cout<<"Play"<<std::endl;
                     (*itr)->play();
                }
                return true;
            }
            else if (ea.getKey()=='p')
            {
                for(ImageStreamList::iterator itr=_imageStreamList.begin();
                    itr!=_imageStreamList.end();
                    ++itr)
                {
                    std::cout<<"Pause"<<std::endl;
                    (*itr)->pause();
                }
                return true;
            }
            else if (ea.getKey()=='r')
            {
                for(ImageStreamList::iterator itr=_imageStreamList.begin();
                    itr!=_imageStreamList.end();
                    ++itr)
                {
                    std::cout<<"Restart"<<std::endl;
                    (*itr)->rewind();
                    (*itr)->play();
                }
                return true;
            }
            else if (ea.getKey()=='l')
            {
                for(ImageStreamList::iterator itr=_imageStreamList.begin();
                    itr!=_imageStreamList.end();
                    ++itr)
                {
                    if ( (*itr)->getLoopingMode() == osg::ImageStream::LOOPING)
                    {
                        std::cout<<"Toggle Looping Off"<<std::endl;
                        (*itr)->setLoopingMode( osg::ImageStream::NO_LOOPING );
                    }
                    else
                    {
                        std::cout<<"Toggle Looping On"<<std::endl;
                        (*itr)->setLoopingMode( osg::ImageStream::LOOPING );
                    }
                }
                return true;
            }
            return false;
        }

        default:
            return false;
    }
}

void MovieEventHandler::getUsage(osg::ApplicationUsage& usage) const
{
    usage.addKeyboardMouseBinding("p","Pause movie");
    usage.addKeyboardMouseBinding("s","Play movie");
    usage.addKeyboardMouseBinding("r","Restart movie");
    usage.addKeyboardMouseBinding("l","Toggle looping of movie");
}

osg::Geometry* createTexturedQuadGeometry(const osg::Vec3& pos,float width,float height, osg::Image* image, bool useTextureRectangle)
{
    if (useTextureRectangle)
    {
        osg::Geometry* pictureQuad = createTexturedQuadGeometry(pos,
                                           osg::Vec3(width,0.0f,0.0f),
                                           osg::Vec3(0.0f,0.0f,height),
                                           0.0f,image->t(), image->s(),0.0f);

        pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0,
                    new osg::TextureRectangle(image),
                    osg::StateAttribute::ON);

        return pictureQuad;
    }
    else
    {
        osg::Geometry* pictureQuad = createTexturedQuadGeometry(pos,
                                           osg::Vec3(width,0.0f,0.0f),
                                           osg::Vec3(0.0f,0.0f,height),
                                           0.0f,1.0f, 1.0f,0.0f);

        osg::Texture2D* texture = new osg::Texture2D(image);
        texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);

        pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0,
                    texture,
                    osg::StateAttribute::ON);

        return pictureQuad;
    }
}

int main(int argc, char** argv)
{
    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    // set up the usage document, in case we need to print out how to use this program.
    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models.");
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");

    bool useTextureRectangle = true;
    bool useShader = false;

    // construct the viewer.
    osgProducer::Viewer viewer(arguments);

    while (arguments.read("--texture2D")) useTextureRectangle=false;
    while (arguments.read("--shader")) useShader=true;

    // set up the value with sensible default event handlers.
    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);

    // register the handler to add keyboard and mosue handling.
    MovieEventHandler* meh = new MovieEventHandler();
    viewer.getEventHandlerList().push_front(meh);

    // get details on keyboard and mouse bindings used by the viewer.
    viewer.getUsage(*arguments.getApplicationUsage());

    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    {
        arguments.getApplicationUsage()->write(std::cout);
        return 1;
    }

    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    osg::Vec3 pos(0.0f,0.0f,0.0f);

    osg::StateSet* stateset = geode->getOrCreateStateSet();
    stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);

    if (useShader)
    {
        //useTextureRectangle = false;

        static const char *shaderSourceTextureRec = {
            "uniform vec4 cutoff_color;\n"
            "uniform samplerRect movie_texture;\n"
            "void main(void)\n"
            "{\n"
            "    vec4 texture_color = textureRect(movie_texture, gl_TexCoord[0]); \n"
            "    if (all(lessThanEqual(texture_color,cutoff_color))) discard; \n"
            "    gl_FragColor = texture_color;\n"
            "}\n"
        };

        static const char *shaderSourceTexture2D = {
            "uniform vec4 cutoff_color;\n"
            "uniform sampler2D movie_texture;\n"
            "void main(void)\n"
            "{\n"
            "    vec4 texture_color = texture2D(movie_texture, gl_TexCoord[0]); \n"
            "    if (all(lessThanEqual(texture_color,cutoff_color))) discard; \n"
            "    gl_FragColor = texture_color;\n"
            "}\n"
        };

        osg::Program* program = new osg::Program;

        program->addShader(new osg::Shader(osg::Shader::FRAGMENT,
                                           useTextureRectangle ? shaderSourceTextureRec : shaderSourceTexture2D));

        stateset->addUniform(new osg::Uniform("cutoff_color",osg::Vec4(0.1f,0.1f,0.1f,1.0f)));
        stateset->addUniform(new osg::Uniform("movie_texture",0));

        stateset->setAttribute(program);

    }

    for(int i=1;i<arguments.argc();++i)
    {
        if (arguments.isString(i))
        {
            osg::Image* image = osgDB::readImageFile(arguments[i]);
            osg::ImageStream* imagestream = dynamic_cast<osg::ImageStream*>(image);
            if (imagestream) imagestream->play();

            if (image)
            {
                geode->addDrawable(createTexturedQuadGeometry(pos,image->s(),image->t(),image, useTextureRectangle));

                pos.z() += image->t()*1.5f;
            }
            else
            {
                std::cout<<"Unable to read file "<<arguments[i]<<std::endl;
            }
        }
    }

    if (geode->getNumDrawables()==0)
    {
        // nothing loaded.
        return 1;
    }

    // pass the model to the MovieEventHandler so it can pick out ImageStream's to manipulate.
    meh->set(geode.get());

    // report any errors if they have occured when parsing the program aguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }

    if (arguments.argc()<=1)
    {
        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
        return 1;
    }

    // any option left unread are converted into errors to write out later.
    arguments.reportRemainingOptionsAsUnrecognized();

    // report any errors if they have occured when parsing the program aguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
    }
/*
    // set up a post swap callback to flush deleted GL objects and compile new GL objects
    for(unsigned int cameraNum=0;cameraNum<viewer.getNumberOfCameras();++cameraNum)
    {
        Producer::Camera* camera=viewer.getCamera(cameraNum);
        camera->addPostSwapCallback(new PostSwapFinishCallback());
    }
*/
    // set the scene to render
    viewer.setSceneData(geode.get());

    // create the windows and run the threads.
    viewer.realize();

    while( !viewer.done() )
    {
        // wait for all cull and draw threads to complete.
        viewer.sync();

        // update the scene by traversing it with the the update visitor which will
        // call all node update callbacks and animations.
        viewer.update();

        // fire off the cull and draw traversals of the scene.
        viewer.frame();

    }

    // wait for all cull and draw threads to complete before exit.
    viewer.sync();

    return 0;

}
<|endoftext|>


<commit_before>/*
 * qtmidi (QtMidi.cpp)
 *  Part of QtMidi (http://github.com/waddlesplash/qtmidi).
 *
 * Copyright (c) 2003-2012 by David G. Slomin
 * Copyright (c) 2012 WaddleSplash
 *
 * 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 <stdio.h>
#include <QtMidi.h>
#include <QThread>
#include <QDateTime>
#include <QCoreApplication>
#include <QtMidiFile.h>

class MidiPlayer : public QThread
{
    Q_OBJECT
public:
    MidiPlayer(QtMidiFile* file)
    {
        midi_file = file;
    }

private:
    QtMidiEvent* midi_file_event;
    QtMidiFile* midi_file;
protected:
    void run()
    {
        qint64 start_time = QDateTime::currentDateTime().toMSecsSinceEpoch();
        QList<QtMidiEvent*> events = midi_file->events();
        foreach(midi_file_event,events)
        {
            if (midi_file_event->type() != QtMidiEvent::Meta)
            {
                qint64 event_time = midi_file->timeFromTick(midi_file_event->tick()) * 1000;
                qint64 current_time = QDateTime::currentDateTime().toMSecsSinceEpoch();

                if (current_time - start_time < event_time)
                {
                    int waitTime = event_time - (current_time - start_time);
                    msleep(waitTime);
                }
                handleEvent();
            }
        }

        QtMidi::closeMidiOut();
    }
private slots:
    void handleEvent()
    {
        if (midi_file_event->type() == QtMidiEvent::SysEx)
        { // TODO: sysex
        }
        else
        {
            qint32 message = midi_file_event->message();
            QtMidi::outSendMsg(message);
        }
    }
};

static void usage(char *program_name)
{
    fprintf(stderr, "Usage: %s -p<port> <MidiFile>\n\n", program_name);
    fprintf(stderr, "Ports:\nID	Name\n----------------\n");
    QMap<QString,QString> vals = QtMidi::outDeviceNames();
    foreach(QString key,vals.keys())
    {
        QString value = vals.value(key);
        fprintf(stderr,key.toUtf8().constData());
        fprintf(stderr,"	");
        fprintf(stderr,value.toUtf8().constData());
        fprintf(stderr,"\n");
    }
    exit(1);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc,argv);

    QString filename = "";
    QString midiOutName = "";
    QtMidiFile* midi_file = new QtMidiFile();

    for(int i = 1; i < argc; i++)
    {
        QString curArg(argv[i]);
        if((curArg == "--help") || (curArg == "-h") || (curArg == "/?") || (curArg == "/help"))
        { usage(argv[0]); }
        else if(curArg.startsWith("-p"))
        {
            midiOutName = curArg.mid(2);
        }
        else if(filename == "")
        { filename = argv[i]; }
        else
        { usage(argv[0]); }
    }

    if((filename == "") || (midiOutName == ""))
    {
        usage(argv[0]);
    }
    midi_file->load(filename);

    QtMidi::initMidiOut(midiOutName);

    MidiPlayer* p = new MidiPlayer(midi_file);
    QObject::connect(p,SIGNAL(finished()),&a,SLOT(quit()));
    p->start();

    return a.exec();
}
#include "playsmf.moc"
<commit_msg>Use fast QElapsedTimer instead of slow QDateTime.<commit_after>/*
 * qtmidi (QtMidi.cpp)
 *  Part of QtMidi (http://github.com/waddlesplash/qtmidi).
 *
 * Copyright (c) 2003-2012 by David G. Slomin
 * Copyright (c) 2012 WaddleSplash
 *
 * 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 <stdio.h>
#include <QThread>
#include <QElapsedTimer>
#include <QCoreApplication>
#include <QtMidi.h>
#include <QtMidiFile.h>

class MidiPlayer : public QThread
{
    Q_OBJECT
public:
    MidiPlayer(QtMidiFile* file)
    {
        midi_file = file;
    }

private:
    QtMidiEvent* midi_file_event;
    QtMidiFile* midi_file;
protected:
    void run()
    {
        QElapsedTimer t;
        t.start();
        QList<QtMidiEvent*> events = midi_file->events();
        foreach(midi_file_event,events)
        {
            if (midi_file_event->type() != QtMidiEvent::Meta)
            {
                qint64 event_time = midi_file->timeFromTick(midi_file_event->tick()) * 1000;

                qint32 wait_time = event_time - t.elapsed();
                if(wait_time > 0) {
                    msleep(waitTime);
                }
                handleEvent();
            }
        }

        QtMidi::closeMidiOut();
    }
private slots:
    void handleEvent()
    {
        if (midi_file_event->type() == QtMidiEvent::SysEx)
        { // TODO: sysex
        }
        else
        {
            qint32 message = midi_file_event->message();
            QtMidi::outSendMsg(message);
        }
    }
};

static void usage(char *program_name)
{
    fprintf(stderr, "Usage: %s -p<port> <MidiFile>\n\n", program_name);
    fprintf(stderr, "Ports:\nID	Name\n----------------\n");
    QMap<QString,QString> vals = QtMidi::outDeviceNames();
    foreach(QString key,vals.keys())
    {
        QString value = vals.value(key);
        fprintf(stderr,key.toUtf8().constData());
        fprintf(stderr,"	");
        fprintf(stderr,value.toUtf8().constData());
        fprintf(stderr,"\n");
    }
    exit(1);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc,argv);

    QString filename = "";
    QString midiOutName = "";
    QtMidiFile* midi_file = new QtMidiFile();

    for(int i = 1; i < argc; i++)
    {
        QString curArg(argv[i]);
        if((curArg == "--help") || (curArg == "-h") || (curArg == "/?") || (curArg == "/help"))
        { usage(argv[0]); }
        else if(curArg.startsWith("-p"))
        {
            midiOutName = curArg.mid(2);
        }
        else if(filename == "")
        { filename = argv[i]; }
        else
        { usage(argv[0]); }
    }

    if((filename == "") || (midiOutName == ""))
    {
        usage(argv[0]);
    }
    midi_file->load(filename);

    QtMidi::initMidiOut(midiOutName);

    MidiPlayer* p = new MidiPlayer(midi_file);
    QObject::connect(p,SIGNAL(finished()),&a,SLOT(quit()));
    p->start();

    return a.exec();
}
#include "playsmf.moc"
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Flowgrammable.org
//
// 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.

#ifndef FREEFLOW_SOCKET_HPP
#define FREEFLOW_SOCKET_HPP

extern "C" {
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
}

#include "../utility/error.hpp"
#include "../proto/ipv4.hpp"
#include "../proto/ipv6.hpp"

namespace freeflow {
namespace socket {

struct Address
{
  enum Type {
    IPv4 = AF_INET,
    IPv6 = AF_INET6
  };

  Address();
  Address(Type t, const std::string n = std::string(), uint16_t p=0);
  Address(ipv4::Address a, uint16_t p=0);
  Address(ipv6::Address a, uint16_t p=0);

  Address(const Address& a);
  Address& operator=(const Address& a);

  union {
    sockaddr_in v4;
    sockaddr_in6 v6;
  };

  Type type;
};

bool operator==(const Address& l, const Address& r);
bool operator!=(const Address& l, const Address& r);

sockaddr* addr(const Address& a);
socklen_t len(const Address& a);
std::string to_string(const Address& a);

struct Socket
{
  enum Transport {
    UDP = SOCK_DGRAM,
    TCP = SOCK_STREAM
  };

  Socket(int f, Transport t, const Address& l, const Address& p);
  Socket(Transport t, Address a = Address());
  Socket(Socket&& s);
  ~Socket();

  Address local;
  Address peer;

  Transport transport;
  int fd;
  int backlog;
};

Error bind(Socket& s, Address a = Address());
Error connect(Socket& s, const Address& a);

Error listen(Socket& s, int backlog=SOMAXCONN);
Socket accept(Socket& s);

int read(Socket& s);
int wriet(Socket& s);
int sendto(Socket& s);
int recvfrom(Socket& s);

Error close(Socket& s);

std::string to_string(const Socket& s);

} // namespace socket
} // namespace freeflow

#include "socket.ipp"

#endif
<commit_msg>Fix: relative path in socket.hpp<commit_after>// Copyright (c) 2013-2014 Flowgrammable.org
//
// 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.

#ifndef FREEFLOW_SOCKET_HPP
#define FREEFLOW_SOCKET_HPP

extern "C" {
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
}

#include "freeflow/utility/error.hpp"
#include "freeflow/proto/ipv4.hpp"
#include "freeflow/proto/ipv6.hpp"

namespace freeflow {
namespace socket {

struct Address
{
  enum Type {
    IPv4 = AF_INET,
    IPv6 = AF_INET6
  };

  Address();
  Address(Type t, const std::string n = std::string(), uint16_t p=0);
  Address(ipv4::Address a, uint16_t p=0);
  Address(ipv6::Address a, uint16_t p=0);

  Address(const Address& a);
  Address& operator=(const Address& a);

  union {
    sockaddr_in v4;
    sockaddr_in6 v6;
  };

  Type type;
};

bool operator==(const Address& l, const Address& r);
bool operator!=(const Address& l, const Address& r);

sockaddr* addr(const Address& a);
socklen_t len(const Address& a);
std::string to_string(const Address& a);

struct Socket
{
  enum Transport {
    UDP = SOCK_DGRAM,
    TCP = SOCK_STREAM
  };

  Socket(int f, Transport t, const Address& l, const Address& p);
  Socket(Transport t, Address a = Address());
  Socket(Socket&& s);
  ~Socket();

  Address local;
  Address peer;

  Transport transport;
  int fd;
  int backlog;
};

Error bind(Socket& s, Address a = Address());
Error connect(Socket& s, const Address& a);

Error listen(Socket& s, int backlog=SOMAXCONN);
Socket accept(Socket& s);

int read(Socket& s);
int wriet(Socket& s);
int sendto(Socket& s);
int recvfrom(Socket& s);

Error close(Socket& s);

std::string to_string(const Socket& s);

} // namespace socket
} // namespace freeflow

#include "socket.ipp"

#endif
<|endoftext|>