nearby: snapshot of cl/313536507

Signed-off-by: Alexey Polyudov <apolyudov@google.com>
Change-Id: I8936b527079074d5c3af5531b9245063767cc4a7
This commit is contained in:
Alexey Polyudov
2020-05-28 00:03:22 -07:00
parent 667bf4ee3b
commit ae1c427b99
334 changed files with 20315 additions and 1487 deletions
+73
View File
@@ -0,0 +1,73 @@
load("//ads/util/non_compile:non_compile.bzl", "cc_with_non_compile_test")
cc_library(
name = "base",
srcs = [
"base64_utils.cc",
"prng.cc",
],
hdrs = [
"base64_utils.h",
"byte_array.h",
"callable.h",
"exception.h",
"input_stream.h",
"listeners.h",
"output_stream.h",
"prng.h",
"runnable.h",
"socket.h",
],
visibility = [
"//core_v2:__subpackages__",
"//platform_v2:__subpackages__",
"//platform_v2/api:__subpackages__",
],
deps = [
"//absl/strings",
"//absl/time",
],
)
cc_library(
name = "util",
srcs = [
"base_pipe.cc",
],
hdrs = [
"base_mutex_lock.h",
"base_pipe.h",
],
visibility = [
"//platform_v2/impl:__subpackages__",
"//platform_v2/public:__pkg__",
],
deps = [
":base",
"//platform_v2/api",
"//absl/base:core_headers",
],
)
cc_test(
name = "platform_base_test",
srcs = [
"byte_array_test.cc",
"prng_test.cc",
],
deps = [
":base",
"//testing/base/public:gunit_main",
],
)
cc_with_non_compile_test(
name = "exception_test",
srcs = [
"exception_test.cc",
],
deps = [
":base",
"//testing/base/public:gunit_main",
],
)
+27
View File
@@ -0,0 +1,27 @@
#include "platform_v2/base/base64_utils.h"
#include "platform_v2/base/byte_array.h"
#include "absl/strings/escaping.h"
namespace location {
namespace nearby {
std::string Base64Utils::Encode(const ByteArray& bytes) {
std::string base64_string;
absl::WebSafeBase64Escape(std::string(bytes), &base64_string);
return base64_string;
}
ByteArray Base64Utils::Decode(absl::string_view base64_string) {
std::string decoded_string;
if (!absl::WebSafeBase64Unescape(base64_string, &decoded_string)) {
return ByteArray();
}
return ByteArray(decoded_string.data(), decoded_string.size());
}
} // namespace nearby
} // namespace location
+19
View File
@@ -0,0 +1,19 @@
#ifndef PLATFORM_V2_BASE_BASE64_UTILS_H_
#define PLATFORM_V2_BASE_BASE64_UTILS_H_
#include "platform_v2/base/byte_array.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
class Base64Utils {
public:
static std::string Encode(const ByteArray& bytes);
static ByteArray Decode(absl::string_view base64_string);
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_BASE64_UTILS_H_
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_V2_BASE_BASE_MUTEX_LOCK_H_
#define PLATFORM_V2_BASE_BASE_MUTEX_LOCK_H_
#include "platform_v2/api/mutex.h"
#include "absl/base/thread_annotations.h"
namespace location {
namespace nearby {
// An RAII mechanism to acquire a Lock over a block of code.
class ABSL_SCOPED_LOCKABLE BaseMutexLock final {
public:
explicit BaseMutexLock(api::Mutex* mutex) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex)
: mutex_(mutex) {
mutex_->Lock();
}
~BaseMutexLock() ABSL_UNLOCK_FUNCTION() { mutex_->Unlock(); }
private:
api::Mutex* mutex_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_BASE_MUTEX_LOCK_H_
+96
View File
@@ -0,0 +1,96 @@
#include "platform_v2/base/base_pipe.h"
#include "platform_v2/api/platform.h"
#include "platform_v2/base/base_mutex_lock.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
namespace location {
namespace nearby {
ExceptionOr<ByteArray> BasePipe::Read(size_t size) {
BaseMutexLock lock(mutex_.get());
// We're done reading all the chunks that were written before the OutputStream
// was closed, so there's nothing to do here other than return an empty chunk
// to serve as an EOF indication to callers.
if (read_all_chunks_) {
return ExceptionOr<ByteArray>{ByteArray{}};
}
while (buffer_.empty() && !input_stream_closed_) {
Exception wait_exception = cond_->Wait();
if (wait_exception.Raised()) {
return ExceptionOr<ByteArray>{wait_exception};
}
}
if (input_stream_closed_) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
ByteArray first_chunk{buffer_.front()};
buffer_.pop_front();
// If we received our sentinel chunk, mark the fact that there cannot
// possibly be any more chunks to read here on in, and return an empty chunk
// to serve as an EOF indication to callers.
if (first_chunk.Empty()) {
read_all_chunks_ = true;
return ExceptionOr<ByteArray>{ByteArray{}};
}
// If first_chunk is small enough to not overshoot the requested 'size', just
// return that.
if (first_chunk.size() <= size) {
return ExceptionOr<ByteArray>{first_chunk};
} else {
// Break first_chunk into 2 parts -- the first one of which (next_chunk)
// will be 'size' bytes long, and will be returned, and the second one of
// which (overflow_chunk) will be re-inserted into buffer_, at the head of
// the queue, to be served up in the next call to read().
ByteArray next_chunk(first_chunk.data(), size);
buffer_.push_front(
ByteArray(first_chunk.data() + size, first_chunk.size() - size));
return ExceptionOr<ByteArray>{next_chunk};
}
}
Exception BasePipe::Write(const ByteArray& data) {
BaseMutexLock lock(mutex_.get());
return WriteLocked(data);
}
void BasePipe::MarkInputStreamClosed() {
BaseMutexLock lock(mutex_.get());
input_stream_closed_ = true;
// Trigger cond_ to unblock a potentially-blocked call to read(), and to let
// it know to return Exception::IO.
cond_->Notify();
}
void BasePipe::MarkOutputStreamClosed() {
BaseMutexLock lock(mutex_.get());
// Write a sentinel null chunk before marking output_stream_closed as true.
WriteLocked(ByteArray{});
output_stream_closed_ = true;
}
Exception BasePipe::WriteLocked(const ByteArray& data) {
if (input_stream_closed_ || output_stream_closed_) {
return {Exception::kIo};
}
buffer_.push_back(data);
// Trigger cond_ to unblock a potentially-blocked call to read(), now that
// there's more data for it to consume.
cond_->Notify();
return {Exception::kSuccess};
}
} // namespace nearby
} // namespace location
+128
View File
@@ -0,0 +1,128 @@
#ifndef PLATFORM_V2_BASE_BASE_PIPE_H_
#define PLATFORM_V2_BASE_BASE_PIPE_H_
#include <cstdint>
#include <deque>
#include <memory>
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/api/mutex.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "absl/base/thread_annotations.h"
namespace location {
namespace nearby {
// Common Pipe implenentation.
// It does not depend on platform implementation, and this allows it to
// be used in the platform implementation itself.
// Concrete class must be derived from it, as follows:
//
// class DerivedPipe : public BasePipe {
// public:
// DerivedPipe() {
// auto mutex = /* construct platform-dependent mutex */;
// auto cond = /* construct platform-dependent condition variable */;
// Setup(std::move(mutex), std::move(cond));
// }
// ~DerivedPipe() override = default;
// DerivedPipe(DerivedPipe&&) = default;
// DerivedPipe& operator=(DerivedPipe&&) = default;
// };
class BasePipe {
public:
static constexpr const size_t kChunkSize = 64 * 1024;
virtual ~BasePipe() = default;
// Pipe is not copyable or movable, because copy/move will invalidate
// references to input and output streams.
// If move is required, Pipe could be wrapped with std::unique_ptr<>.
BasePipe(BasePipe&&) = delete;
BasePipe& operator=(BasePipe&&) = delete;
// Get...() methods return references to input and output steam facades.
// It is safe to call Get...() methods multiple times.
InputStream& GetInputStream() { return input_stream_; }
OutputStream& GetOutputStream() { return output_stream_; }
protected:
BasePipe() = default;
void Setup(std::unique_ptr<api::Mutex> mutex,
std::unique_ptr<api::ConditionVariable> cond) {
mutex_ = std::move(mutex);
cond_ = std::move(cond);
}
private:
class BasePipeInputStream : public InputStream {
public:
explicit BasePipeInputStream(BasePipe* pipe) : pipe_(pipe) {}
~BasePipeInputStream() override { DoClose(); }
ExceptionOr<ByteArray> Read(std::int64_t size) override {
return pipe_->Read(size);
}
Exception Close() override {
return DoClose();
}
private:
Exception DoClose() {
pipe_->MarkInputStreamClosed();
return {Exception::kSuccess};
}
BasePipe* pipe_;
};
class BasePipeOutputStream : public OutputStream {
public:
explicit BasePipeOutputStream(BasePipe* pipe) : pipe_(pipe) {}
~BasePipeOutputStream() override { DoClose(); }
Exception Write(const ByteArray& data) override {
return pipe_->Write(data);
}
Exception Flush() override { return {Exception::kSuccess}; }
Exception Close() override {
return DoClose();
}
private:
Exception DoClose() {
pipe_->MarkOutputStreamClosed();
return {Exception::kSuccess};
}
BasePipe* pipe_;
};
ExceptionOr<ByteArray> Read(size_t size) ABSL_LOCKS_EXCLUDED(mutex_);
Exception Write(const ByteArray& data) ABSL_LOCKS_EXCLUDED(mutex_);
void MarkInputStreamClosed() ABSL_LOCKS_EXCLUDED(mutex_);
void MarkOutputStreamClosed() ABSL_LOCKS_EXCLUDED(mutex_);
Exception WriteLocked(const ByteArray& data)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Order of declaration matters:
// - mutex must be defined before condvar;
// - input & output streams must be after both mutex and condvar.
bool input_stream_closed_ ABSL_GUARDED_BY(mutex_) = false;
bool output_stream_closed_ ABSL_GUARDED_BY(mutex_) = false;
bool read_all_chunks_ ABSL_GUARDED_BY(mutex_) = false;
std::deque<ByteArray> ABSL_GUARDED_BY(mutex_) buffer_;
std::unique_ptr<api::Mutex> mutex_;
std::unique_ptr<api::ConditionVariable> cond_;
BasePipeInputStream input_stream_{this};
BasePipeOutputStream output_stream_{this};
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_BASE_PIPE_H_
+81
View File
@@ -0,0 +1,81 @@
#ifndef PLATFORM_V2_BASE_BYTE_ARRAY_H_
#define PLATFORM_V2_BASE_BYTE_ARRAY_H_
#include <cstdint>
#include <string>
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
class ByteArray {
public:
// Create an empty ByteArray
ByteArray() = default;
ByteArray(const ByteArray&) = default;
ByteArray& operator=(const ByteArray&) = default;
ByteArray(ByteArray&&) = default;
ByteArray& operator=(ByteArray&&) = default;
// Create ByteArray from string.
explicit ByteArray(absl::string_view source) { data_ = source; }
// Create default-initialized ByteArray of a given size.
explicit ByteArray(size_t size) { SetData(size); }
// Create value-initialized ByteArray of a given size.
ByteArray(const char* data, size_t size) { SetData(data, size); }
// Assign a new value to this ByteArray, as a copy of data, with a given size.
void SetData(const char* data, size_t size) {
if (data == nullptr) {
size = 0;
}
data_.assign(data, size);
}
// Assign a new value of a given size to this ByteArray
// (as a repeated char value).
void SetData(size_t size, char value = 0) { data_.assign(size, value); }
// Returns true, if changes were performed to container, false otherwise.
bool CopyAt(size_t offset, const ByteArray& from, size_t source_offset = 0) {
if (offset >= size()) return false;
if (source_offset >= from.size()) return false;
memcpy(data() + offset, from.data() + source_offset,
std::min(size() - offset, from.size() - source_offset));
return true;
}
char* data() { return &data_[0]; }
const char* data() const { return data_.data(); }
size_t size() const { return data_.size(); }
bool Empty() const { return data_.empty(); }
friend bool operator==(const ByteArray& lhs, const ByteArray& rhs);
friend bool operator!=(const ByteArray& lhs, const ByteArray& rhs);
friend bool operator<(const ByteArray& lhs, const ByteArray& rhs);
explicit operator std::string() const { return data_; }
private:
std::string data_;
};
inline bool operator==(const ByteArray& lhs, const ByteArray& rhs) {
return lhs.data_ == rhs.data_;
}
inline bool operator!=(const ByteArray& lhs, const ByteArray& rhs) {
return !(lhs == rhs);
}
inline bool operator<(const ByteArray& lhs, const ByteArray& rhs) {
return lhs.data_ < rhs.data_;
}
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_BYTE_ARRAY_H_
+68
View File
@@ -0,0 +1,68 @@
#include "platform_v2/base/byte_array.h"
#include <cstring>
#include "gtest/gtest.h"
namespace {
using location::nearby::ByteArray;
TEST(ByteArrayTest, DefaultSizeIsZero) {
ByteArray bytes;
EXPECT_EQ(0, bytes.size());
}
TEST(ByteArrayTest, DefaultIsEmpty) {
ByteArray bytes;
EXPECT_TRUE(bytes.Empty());
}
TEST(ByteArrayTest, NullArrayIsEmpty) {
ByteArray bytes{nullptr, 5};
EXPECT_TRUE(bytes.Empty());
}
TEST(ByteArrayTest, CopyAtDoesNotExtendArray) {
ByteArray v1("12345");
ByteArray v2("ABCDEFGH");
EXPECT_TRUE(v2.CopyAt(/*offset=*/5, v1));
EXPECT_TRUE(v2.CopyAt(/*offset=*/1, v1, /*source_offset=*/3));
EXPECT_EQ(v2, ByteArray("A45DE123"));
}
TEST(ByteArrayTest, CopyAtOutOfBoundsIsIgnored) {
ByteArray v1("12345");
ByteArray v2("ABCDEFGH");
// Try to do an out-of-bounds read.
EXPECT_FALSE(v2.CopyAt(/* offset=*/5, v1, /*source_offset=*/10));
// Try to do an out-of-bounds write.
EXPECT_FALSE(v2.CopyAt(/* offset=*/9, v1));
EXPECT_EQ(v2, ByteArray("ABCDEFGH"));
}
TEST(ByteArrayTest, SetFromString) {
std::string setup("setup_test");
ByteArray bytes{setup}; // array initialized with a copy of string.
EXPECT_EQ(setup.size(), bytes.size());
EXPECT_EQ(std::string(bytes), setup);
}
TEST(ByteArrayTest, SetExplicitSize) {
constexpr size_t kArraySize = 10;
char reference[kArraySize]{};
ByteArray bytes{kArraySize}; // array of size 10, zero-initialized.
EXPECT_EQ(kArraySize, bytes.size());
EXPECT_EQ(0, memcmp(bytes.data(), reference, kArraySize));
}
TEST(ByteArrayTest, SetExplicitData) {
constexpr static const char message[]{"test_message"};
constexpr size_t kMessageSize = sizeof(message);
ByteArray bytes{message, kMessageSize};
EXPECT_EQ(kMessageSize, bytes.size());
EXPECT_NE(message, bytes.data());
EXPECT_EQ(0, memcmp(message, bytes.data(), kMessageSize));
}
} // namespace
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_V2_BASE_CALLABLE_H_
#define PLATFORM_V2_BASE_CALLABLE_H_
#include <functional>
#include "platform_v2/base/exception.h"
namespace location {
namespace nearby {
// The Callable is and object intended to be executed by a thread, that is able
// to return a value of specified type T.
// It must be invokable without arguments. It must return a value implicitly
// convertible to ExceptionOr<T>.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html
template <typename T>
using Callable = std::function<ExceptionOr<T>()>;
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_CALLABLE_H_
+97
View File
@@ -0,0 +1,97 @@
#ifndef PLATFORM_V2_BASE_EXCEPTION_H_
#define PLATFORM_V2_BASE_EXCEPTION_H_
#include <type_traits>
#include <utility>
namespace location {
namespace nearby {
struct Exception {
enum Value : int {
kFailed = -1, // Initial value of Exception; any unknown error.
kSuccess = 0, // No exception.
kIo = 1, // IO Error happened.
kInterrupted = 2, // Operation was interrupted.
kInvalidProtocolBuffer = 3, // Couldn't parse.
kExecution = 4, // Couldn't execute.
kTimeout = 5, // Operarion did not finish within specified time.
};
bool Ok() const { return value == kSuccess; }
bool Raised() const { return !Ok(); }
bool Raised(Value value) const { return this->value == value; }
Value value{kFailed};
};
constexpr inline bool operator==(const Exception& a, const Exception& b) {
return a.value == b.value;
}
constexpr inline bool operator!=(const Exception& a, const Exception& b) {
return !(a == b);
}
// ExceptionOr provides experience similar to StatusOr<T> used in
// Google Cloud API, see:
// https://googleapis.github.io/google-cloud-cpp/0.7.0/common/status__or_8h_source.html
//
// If ok() returns true, result() is a usable return value. Otherwise,
// exception() explains why such a value is not present.
//
// A typical pattern of usage is as follows:
//
// if (!e.ok()) {
// if (Exception::EXCEPTION_TYPE_1 == e.exception()) {
// // Handle Exception::EXCEPTION_TYPE_1.
// } else if (Exception::EXCEPTION_TYPE_2 == e.exception()) {
// // Handle Exception::EXCEPTION_TYPE_2.
// }
//
// return;
// }
//
// // Use e.result().
template <typename T>
class ExceptionOr {
public:
ExceptionOr() = default;
explicit ExceptionOr(T&& result)
: result_{std::move(result)},
exception_{Exception::kSuccess} {} // NOLINT
explicit ExceptionOr(const T& result)
: result_{result}, exception_{Exception::kSuccess} {} // NOLINT
ExceptionOr(Exception::Value exception) : exception_{exception} {} // NOLINT
ExceptionOr(Exception exception) : exception_{exception} {} // NOLINT
// If there exists explicit conversion from from U to T,
// then allow explicit conversion from ExceptionOr<U> to ExceptionOr<T>.
template <typename U, typename = std::void_t<decltype(T{std::declval<U>()})>>
explicit ExceptionOr<T>(ExceptionOr<U> value) {
if (!value.ok()) {
exception_ = value.GetException();
} else {
result_ = T{std::move(value.result())};
exception_ = Exception{Exception::kSuccess};
}
}
bool ok() const { return exception_.value == Exception::kSuccess; }
T& result() & { return result_; }
const T& result() const& { return result_; }
T&& result() && { return std::move(result_); }
const T&& result() const&& { return std::move(result_); }
Exception::Value exception() const { return exception_.value; }
T GetResult() const { return result_; }
Exception GetException() const { return exception_; }
private:
T result_{};
Exception exception_{Exception::kFailed};
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_EXCEPTION_H_
+106
View File
@@ -0,0 +1,106 @@
#include "platform_v2/base/exception.h"
#include <vector>
#include "platform_v2/base/exception_test.nc.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location::nearby {
TEST(ExceptionOr, Result_Copy_NonConst) {
ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Expect a copy when not explicitly moving the result.
std::vector<int> copy = exception_or_vector.result();
EXPECT_FALSE(copy.empty());
EXPECT_FALSE(exception_or_vector.result().empty());
// Modifying |exception_or_vector| should not affect the copy.
exception_or_vector.result().clear();
EXPECT_FALSE(copy.empty());
}
TEST(ExceptionOr, Result_Copy_Const) {
const ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Expect a copy when not explicitly moving the result.
std::vector<int> copy = exception_or_vector.result();
EXPECT_FALSE(copy.empty());
EXPECT_FALSE(exception_or_vector.result().empty());
}
TEST(ExceptionOr, Result_Reference_NonConst) {
ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Getting a reference should not modify the source.
std::vector<int>& reference = exception_or_vector.result();
EXPECT_FALSE(reference.empty());
EXPECT_FALSE(exception_or_vector.result().empty());
// Modifying |exception_or_vector| should reflect in the reference.
exception_or_vector.result().clear();
EXPECT_TRUE(reference.empty());
}
TEST(ExceptionOr, Result_Reference_Const) {
const ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Getting a reference should not modify the source.
const std::vector<int>& reference = exception_or_vector.result();
EXPECT_FALSE(reference.empty());
EXPECT_FALSE(exception_or_vector.result().empty());
}
TEST(ExceptionOr, Result_Move_NonConst) {
ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Moving the result should clear the source.
std::vector<int> moved = std::move(exception_or_vector).result();
EXPECT_FALSE(moved.empty());
}
TEST(ExceptionOr, Result_Move_Const) {
const ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Moving const rvalue reference will result in a copy.
std::vector<int> moved = std::move(exception_or_vector).result();
EXPECT_FALSE(moved.empty());
}
TEST(ExceptionOr, ExplicitConversionWorks) {
class A {
public:
A() = default;
};
class B {
public:
B() = default;
explicit B(A) {}
};
ExceptionOr<A> a(A{});
ExceptionOr<B> b(a);
EXPECT_TRUE(a.ok());
EXPECT_TRUE(b.ok());
}
TEST(ExceptionOr, ExplicitConversionFailsToCompile) {
class A {
public:
A() = default;
};
class B {
public:
B() = default;
};
ExceptionOr<A> a(A{});
EXPECT_NON_COMPILE("no matching constructor", { ExceptionOr<B> b(a); });
}
} // namespace location::nearby
+28
View File
@@ -0,0 +1,28 @@
#ifndef PLATFORM_V2_BASE_INPUT_STREAM_H_
#define PLATFORM_V2_BASE_INPUT_STREAM_H_
#include <cstdint>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
namespace location {
namespace nearby {
// An InputStream represents an input stream of bytes.
//
// https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html
class InputStream {
public:
virtual ~InputStream() = default;
// throws Exception::kIo
virtual ExceptionOr<ByteArray> Read(std::int64_t size) = 0;
// throws Exception::kIo
virtual Exception Close() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_INPUT_STREAM_H_
+20
View File
@@ -0,0 +1,20 @@
#ifndef PLATFORM_V2_BASE_LISTENERS_H_
#define PLATFORM_V2_BASE_LISTENERS_H_
#include <functional>
namespace location {
namespace nearby {
// Provides default-initialization with a valid empty method,
// instead of nullptr. This allows partial initialization
// of a set of listeners.
template <typename... Args>
constexpr std::function<void(Args...)> DefaultCallback() {
return std::function<void(Args...)>{[](Args...) {}};
}
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_LISTENERS_H_
+25
View File
@@ -0,0 +1,25 @@
#ifndef PLATFORM_V2_BASE_OUTPUT_STREAM_H_
#define PLATFORM_V2_BASE_OUTPUT_STREAM_H_
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
namespace location {
namespace nearby {
// An OutputStream represents an output stream of bytes.
//
// https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html
class OutputStream {
public:
virtual ~OutputStream() = default;
virtual Exception Write(const ByteArray& data) = 0; // throws Exception::kIo
virtual Exception Flush() = 0; // throws Exception::kIo
virtual Exception Close() = 0; // throws Exception::kIo
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_OUTPUT_STREAM_H_
+45
View File
@@ -0,0 +1,45 @@
#include "platform_v2/base/prng.h"
#include <limits>
#include "absl/time/clock.h"
namespace location {
namespace nearby {
#define UNSIGNED_INT_BITMASK (std::numeric_limits<unsigned int>::max())
Prng::Prng() {
// absl::GetCurrentTimeNanos() returns 64 bits, but srand() wants an unsigned
// int, so we may have to lose some of those 64 bits.
//
// The lower bits of the current-time-in-nanos are likely to have more entropy
// than the upper bits, so choose the former.
srand(static_cast<unsigned int>(absl::GetCurrentTimeNanos() &
UNSIGNED_INT_BITMASK));
}
Prng::~Prng() {
// Nothing to do.
}
#define RANDOM_BYTE (rand() & 0x0FF) // NOLINT
std::int32_t Prng::NextInt32() {
return (static_cast<std::int32_t>(RANDOM_BYTE) << 24) |
(static_cast<std::int32_t>(RANDOM_BYTE) << 16) |
(static_cast<std::int32_t>(RANDOM_BYTE) << 8) |
(static_cast<std::int32_t>(RANDOM_BYTE));
}
std::uint32_t Prng::NextUint32() {
return static_cast<std::uint32_t>(NextInt32());
}
std::int64_t Prng::NextInt64() {
return (static_cast<std::int64_t>(NextInt32()) << 32) |
(static_cast<std::int64_t>(NextInt32()));
}
} // namespace nearby
} // namespace location
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_V2_BASE_PRNG_H_
#define PLATFORM_V2_BASE_PRNG_H_
#include <cstdint>
namespace location {
namespace nearby {
// A (non-cryptographic) pseudo-random number generator.
class Prng {
public:
Prng();
~Prng();
std::int32_t NextInt32();
std::uint32_t NextUint32();
std::int64_t NextInt64();
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_PRNG_H_
+27
View File
@@ -0,0 +1,27 @@
#include "platform_v2/base/prng.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
TEST(PrngTest, NextInt32) {
std::int32_t i = Prng().NextInt32();
EXPECT_LE(i, std::numeric_limits<std::int32_t>::max());
EXPECT_GE(i, std::numeric_limits<std::int32_t>::min());
}
TEST(PrngTest, NextUInt32) {
std::uint32_t i = Prng().NextUint32();
EXPECT_LE(i, std::numeric_limits<std::uint32_t>::max());
EXPECT_GE(i, std::numeric_limits<std::uint32_t>::min());
}
TEST(PrngTest, NextInt64) {
std::int64_t i = Prng().NextInt64();
EXPECT_LE(i, std::numeric_limits<std::int64_t>::max());
EXPECT_GE(i, std::numeric_limits<std::int64_t>::min());
}
} // namespace nearby
} // namespace location
+19
View File
@@ -0,0 +1,19 @@
#ifndef PLATFORM_V2_BASE_RUNNABLE_H_
#define PLATFORM_V2_BASE_RUNNABLE_H_
#include <functional>
namespace location {
namespace nearby {
// The Runnable is an object intended to be executed by a thread.
// It must be invokable without arguments. It must return void.
//
// https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html
using Runnable = std::function<void()>;
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_RUNNABLE_H_
+25
View File
@@ -0,0 +1,25 @@
#ifndef PLATFORM_V2_BASE_SOCKET_H_
#define PLATFORM_V2_BASE_SOCKET_H_
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
namespace location {
namespace nearby {
// A socket is an endpoint for communication between two machines.
//
// https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html
class Socket {
public:
virtual ~Socket() = default;
virtual InputStream& GetInputStream() = 0;
virtual OutputStream& GetOutputStream() = 0;
virtual void Close() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_SOCKET_H_