aboutsummaryrefslogtreecommitdiffstats
path: root/libtransport/src/utils
diff options
context:
space:
mode:
authorMauro Sardara <msardara@cisco.com>2020-02-21 11:52:28 +0100
committerMauro Sardara <msardara@cisco.com>2020-02-26 13:19:16 +0100
commitf4433f28b509a9f67ca85d79000ccf9c2f4b7a24 (patch)
tree0f754bc9d8222f3ace11849165753acd85be3b38 /libtransport/src/utils
parent0e7669445b6be1163189521eabed7dd0124043c8 (diff)
[HICN-534] Major rework on libtransport organization
Change-Id: I361b83a18b4fd59be136d5f0817fc28e17e89884 Signed-off-by: Mauro Sardara <msardara@cisco.com>
Diffstat (limited to 'libtransport/src/utils')
-rw-r--r--libtransport/src/utils/CMakeLists.txt53
-rw-r--r--libtransport/src/utils/content_store.cc122
-rw-r--r--libtransport/src/utils/content_store.h76
-rw-r--r--libtransport/src/utils/daemonizator.cc76
-rw-r--r--libtransport/src/utils/deadline_timer.h114
-rw-r--r--libtransport/src/utils/epoll_event_reactor.cc187
-rw-r--r--libtransport/src/utils/epoll_event_reactor.h80
-rw-r--r--libtransport/src/utils/event_reactor.h37
-rw-r--r--libtransport/src/utils/event_thread.h99
-rw-r--r--libtransport/src/utils/fd_deadline_timer.h129
-rw-r--r--libtransport/src/utils/log.cc1403
-rw-r--r--libtransport/src/utils/membuf.cc867
-rw-r--r--libtransport/src/utils/memory_pool_allocator.h152
-rw-r--r--libtransport/src/utils/min_filter.h56
-rw-r--r--libtransport/src/utils/stream_buffer.h31
-rw-r--r--libtransport/src/utils/string_tokenizer.cc47
-rw-r--r--libtransport/src/utils/suffix_strategy.h165
-rw-r--r--libtransport/src/utils/test.h46
-rw-r--r--libtransport/src/utils/uri.cc122
19 files changed, 3862 insertions, 0 deletions
diff --git a/libtransport/src/utils/CMakeLists.txt b/libtransport/src/utils/CMakeLists.txt
new file mode 100644
index 000000000..88d451b6b
--- /dev/null
+++ b/libtransport/src/utils/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (c) 2017-2019 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/string_tokenizer.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/uri.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/log.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/membuf.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/content_store.cc
+)
+
+
+list(APPEND HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/event_reactor.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/min_filter.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/stream_buffer.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/suffix_strategy.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/event_thread.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/content_store.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/deadline_timer.h
+)
+
+if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
+ list(APPEND HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/fd_deadline_timer.h
+ )
+
+ list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/epoll_event_reactor.cc
+ )
+endif()
+
+if(NOT WIN32)
+ list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemonizator.cc
+ )
+endif()
+
+set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
+set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
diff --git a/libtransport/src/utils/content_store.cc b/libtransport/src/utils/content_store.cc
new file mode 100644
index 000000000..cb3db6d94
--- /dev/null
+++ b/libtransport/src/utils/content_store.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/interest.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/utils/log.h>
+
+#include <utils/content_store.h>
+
+namespace utils {
+
+ContentStore::ContentStore(std::size_t max_packets)
+ : max_content_store_size_(max_packets) {}
+
+ContentStore::~ContentStore() {}
+
+void ContentStore::insert(
+ const std::shared_ptr<ContentObject> &content_object) {
+ if (max_content_store_size_ == 0) {
+ return;
+ }
+
+ utils::SpinLock::Acquire locked(cs_mutex_);
+
+ if (TRANSPORT_EXPECT_FALSE(content_store_hash_table_.size() !=
+ fifo_list_.size())) {
+ TRANSPORT_LOGW("Inconsistent size!!!!");
+ TRANSPORT_LOGW("Hash Table: %zu |||| FIFO List: %zu",
+ content_store_hash_table_.size(), fifo_list_.size());
+ }
+
+ if (content_store_hash_table_.size() >= max_content_store_size_) {
+ content_store_hash_table_.erase(fifo_list_.back());
+ fifo_list_.pop_back();
+ }
+
+ // Insert new item
+ auto it = content_store_hash_table_.find(content_object->getName());
+ if (it != content_store_hash_table_.end()) {
+ fifo_list_.erase(it->second.second);
+ content_store_hash_table_.erase(content_object->getName());
+ }
+
+ fifo_list_.push_front(std::cref(content_object->getName()));
+ auto pos = fifo_list_.begin();
+ content_store_hash_table_[content_object->getName()] = ContentStoreEntry(
+ ObjectTimeEntry(content_object, std::chrono::steady_clock::now()), pos);
+}
+
+const std::shared_ptr<ContentObject> ContentStore::find(
+ const Interest &interest) {
+ utils::SpinLock::Acquire locked(cs_mutex_);
+
+ std::shared_ptr<ContentObject> ret = empty_reference_;
+ auto it = content_store_hash_table_.find(interest.getName());
+ if (it != content_store_hash_table_.end()) {
+ auto content_lifetime = it->second.first.first->getLifetime();
+ auto time_passed_since_creation =
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now() - it->second.first.second)
+ .count();
+
+ if (time_passed_since_creation > content_lifetime) {
+ fifo_list_.erase(it->second.second);
+ content_store_hash_table_.erase(it);
+ } else {
+ ret = it->second.first.first;
+ }
+ }
+
+ return ret;
+}
+
+void ContentStore::erase(const Name &exact_name) {
+ utils::SpinLock::Acquire locked(cs_mutex_);
+ auto it = content_store_hash_table_.find(exact_name);
+ fifo_list_.erase(it->second.second);
+ content_store_hash_table_.erase(exact_name);
+}
+
+void ContentStore::setLimit(size_t max_packets) {
+ utils::SpinLock::Acquire locked(cs_mutex_);
+ max_content_store_size_ = max_packets;
+}
+
+std::size_t ContentStore::getLimit() const {
+ utils::SpinLock::Acquire locked(cs_mutex_);
+ return max_content_store_size_;
+}
+
+std::size_t ContentStore::size() const {
+ utils::SpinLock::Acquire locked(cs_mutex_);
+ return content_store_hash_table_.size();
+}
+
+void ContentStore::printContent() {
+ for (auto &item : content_store_hash_table_) {
+ if (item.second.first.first->getPayloadType() ==
+ transport::core::PayloadType::MANIFEST) {
+ TRANSPORT_LOGI("Manifest: %s",
+ item.second.first.first->getName().toString().c_str());
+ } else {
+ TRANSPORT_LOGI("Data Packet: %s",
+ item.second.first.first->getName().toString().c_str());
+ }
+ }
+}
+
+} // end namespace utils \ No newline at end of file
diff --git a/libtransport/src/utils/content_store.h b/libtransport/src/utils/content_store.h
new file mode 100644
index 000000000..03ce76f42
--- /dev/null
+++ b/libtransport/src/utils/content_store.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/utils/spinlock.h>
+
+#include <mutex>
+
+namespace transport {
+
+namespace core {
+class Name;
+class ContentObject;
+class Interest;
+} // namespace core
+
+} // namespace transport
+
+namespace utils {
+
+using Name = transport::core::Name;
+using ContentObject = transport::core::ContentObject;
+using Interest = transport::core::Interest;
+
+typedef std::pair<std::shared_ptr<ContentObject>,
+ std::chrono::steady_clock::time_point>
+ ObjectTimeEntry;
+typedef std::pair<ObjectTimeEntry,
+ std::list<std::reference_wrapper<const Name>>::iterator>
+ ContentStoreEntry;
+typedef std::list<std::reference_wrapper<const Name>> FIFOList;
+typedef std::unordered_map<Name, ContentStoreEntry> ContentStoreHashTable;
+
+class ContentStore {
+ public:
+ explicit ContentStore(std::size_t max_packets = (1 << 16));
+
+ ~ContentStore();
+
+ void insert(const std::shared_ptr<ContentObject> &content_object);
+
+ const std::shared_ptr<ContentObject> find(const Interest &interest);
+
+ void erase(const Name &exact_name);
+
+ void setLimit(size_t max_packets);
+
+ size_t getLimit() const;
+
+ size_t size() const;
+
+ void printContent();
+
+ private:
+ ContentStoreHashTable content_store_hash_table_;
+ FIFOList fifo_list_;
+ std::shared_ptr<ContentObject> empty_reference_;
+ // Must be atomic
+ std::atomic_size_t max_content_store_size_;
+ mutable utils::SpinLock cs_mutex_;
+};
+
+} // end namespace utils \ No newline at end of file
diff --git a/libtransport/src/utils/daemonizator.cc b/libtransport/src/utils/daemonizator.cc
new file mode 100644
index 000000000..c51a68d14
--- /dev/null
+++ b/libtransport/src/utils/daemonizator.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _WIN32
+#include <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/utils/daemonizator.h>
+#include <hicn/transport/utils/log.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace utils {
+
+void Daemonizator::daemonize(bool close_fds) {
+ pid_t process_id = 0;
+ pid_t sid = 0;
+
+ // Create child process
+ process_id = fork();
+
+ // Indication of fork() failure
+ if (process_id < 0) {
+ throw errors::RuntimeException("Fork failed.");
+ }
+
+ // PARENT PROCESS. Need to kill it.
+ if (process_id > 0) {
+ TRANSPORT_LOGE("Process id of child process %d", process_id);
+ // return success in exit status
+ exit(EXIT_SUCCESS);
+ }
+
+ // unmask the file mode
+ umask(0);
+
+ // set new session
+ sid = setsid();
+ if (sid < 0) {
+ // Return failure
+ exit(EXIT_FAILURE);
+ }
+
+ // Change the current working directory to root.
+ int ret = chdir("/");
+
+ if (ret < 0) {
+ throw errors::RuntimeException("Error changing working directory to root");
+ }
+
+ // Close stdin. Redirect stdout and stderr to file if possible
+
+ if (close_fds) {
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+
+ close(STDIN_FILENO);
+
+ // Really start application
+}
+
+} // namespace utils
+
+#endif
diff --git a/libtransport/src/utils/deadline_timer.h b/libtransport/src/utils/deadline_timer.h
new file mode 100644
index 000000000..5187754f0
--- /dev/null
+++ b/libtransport/src/utils/deadline_timer.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/event_reactor.h>
+
+#include <chrono>
+#include <cstddef>
+#include <cstring>
+#include <utility>
+
+namespace std {
+namespace chrono {
+namespace detail {
+
+template <typename From, typename To>
+struct posix_duration_cast;
+
+// chrono -> timespec caster
+template <typename Rep, typename Period>
+struct posix_duration_cast<std::chrono::duration<Rep, Period>,
+ struct timespec> {
+ static struct timespec cast(std::chrono::duration<Rep, Period> const &d) {
+ struct timespec tv;
+
+ std::chrono::seconds const sec =
+ std::chrono::duration_cast<std::chrono::seconds>(d);
+
+ tv.tv_sec = sec.count();
+ tv.tv_nsec =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec).count();
+
+ return tv;
+ }
+};
+
+// timespec -> chrono caster
+template <typename Rep, typename Period>
+struct posix_duration_cast<struct timespec,
+ std::chrono::duration<Rep, Period>> {
+ static std::chrono::duration<Rep, Period> cast(struct timespec const &tv) {
+ return std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
+ std::chrono::seconds(tv.tv_sec) + std::chrono::nanoseconds(tv.tv_nsec));
+ }
+};
+
+} // namespace detail
+
+// chrono -> timespec
+template <typename T, typename Rep, typename Period>
+auto duration_cast(std::chrono::duration<Rep, Period> const &d) ->
+ typename std::enable_if<std::is_same<T, struct timespec>::value,
+ struct timespec>::type {
+ return detail::posix_duration_cast<std::chrono::duration<Rep, Period>,
+ timespec>::cast(d);
+}
+
+// timespec -> chrono
+template <typename Duration>
+Duration duration_cast(struct timespec const &tv) {
+ return detail::posix_duration_cast<struct timespec, Duration>::cast(tv);
+}
+
+} // namespace chrono
+} // namespace std
+
+namespace utils {
+
+template <typename Implementation>
+class DeadlineTimer {
+ public:
+ virtual ~DeadlineTimer() = default;
+
+ template <typename WaitHandler>
+ void asyncWait(WaitHandler &&callback) {
+ static_cast<Implementation *>(this)->asyncWaitImpl(
+ std::forward<WaitHandler>(callback));
+ }
+
+ void wait() { static_cast<Implementation *>(this)->waitImpl(); }
+
+ template <typename T, typename R>
+ void expiresFromNow(std::chrono::duration<T, R> &&duration) {
+ static_cast<Implementation *>(this)->expiresFromNowImpl(
+ std::forward<std::chrono::duration<T, R>>(duration));
+ }
+
+ template <typename TimePoint,
+ typename = typename std::enable_if<
+ std::is_same<std::remove_reference_t<TimePoint>,
+ std::chrono::steady_clock::time_point>::value,
+ TimePoint>::type>
+ void expiresAt(TimePoint &&time_point) {
+ static_cast<Implementation *>(this)->expiresAtImpl(
+ std::forward<TimePoint>(time_point));
+ }
+
+ void cancel() { static_cast<Implementation *>(this)->cancelImpl(); }
+};
+
+} // namespace utils
diff --git a/libtransport/src/utils/epoll_event_reactor.cc b/libtransport/src/utils/epoll_event_reactor.cc
new file mode 100644
index 000000000..0e6590d0e
--- /dev/null
+++ b/libtransport/src/utils/epoll_event_reactor.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/utils/branch_prediction.h>
+
+#include <utils/epoll_event_reactor.h>
+#include <utils/fd_deadline_timer.h>
+
+#include <signal.h>
+#include <unistd.h>
+#include <iostream>
+
+namespace utils {
+
+EpollEventReactor::EpollEventReactor()
+ : epoll_fd_(epoll_create(20000)), run_event_loop_(true) {}
+
+EpollEventReactor::~EpollEventReactor() { close(epoll_fd_); }
+
+int EpollEventReactor::addFileDescriptor(int fd, uint32_t events) {
+ if (TRANSPORT_EXPECT_FALSE(fd < 0)) {
+ TRANSPORT_LOGE("invalid fd %d", fd);
+ return -1;
+ }
+
+ struct epoll_event evt;
+ std::memset(&evt, 0, sizeof(evt));
+ evt.events = events;
+ evt.data.fd = fd;
+
+ if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &evt) <
+ 0)) {
+ TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+int EpollEventReactor::modFileDescriptor(int fd, uint32_t events) {
+ if (TRANSPORT_EXPECT_FALSE(fd < 0)) {
+ TRANSPORT_LOGE("invalid fd %d", fd);
+ return -1;
+ }
+
+ struct epoll_event evt;
+ memset(&evt, 0, sizeof(evt));
+ evt.events = events;
+ evt.data.fd = fd;
+
+ if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &evt) <
+ 0)) {
+ TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+std::size_t EpollEventReactor::mapSize() { return event_callback_map_.size(); }
+
+int EpollEventReactor::delFileDescriptor(int fd) {
+ if (TRANSPORT_EXPECT_FALSE(fd < 0)) {
+ TRANSPORT_LOGE("invalid fd %d", fd);
+ return -1;
+ }
+
+ struct epoll_event evt;
+ memset(&evt, 0, sizeof(evt));
+
+ if (TRANSPORT_EXPECT_FALSE(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &evt) <
+ 0)) {
+ TRANSPORT_LOGE("epoll_ctl: %s fd %d", strerror(errno), fd);
+ return -1;
+ }
+
+ utils::SpinLock::Acquire locked(event_callback_map_lock_);
+ event_callback_map_.erase(fd);
+
+ return 0;
+}
+
+void EpollEventReactor::runEventLoop(int timeout) {
+ Event evt[128];
+ int en = 0;
+ EventCallbackMap::iterator it;
+ EventCallback callback;
+
+ // evt.events = EPOLLIN | EPOLLOUT;
+ sigset_t sigset;
+ sigemptyset(&sigset);
+
+ while (run_event_loop_) {
+ memset(&evt, 0, sizeof(evt));
+
+ en = epoll_pwait(epoll_fd_, evt, 128, timeout, &sigset);
+
+ if (TRANSPORT_EXPECT_FALSE(en < 0)) {
+ TRANSPORT_LOGE("epoll_pwait: %s", strerror(errno));
+ return;
+ }
+
+ for (int i = 0; i < en; i++) {
+ if (evt[i].data.fd > 0) {
+ {
+ utils::SpinLock::Acquire locked(event_callback_map_lock_);
+ it = event_callback_map_.find(evt[i].data.fd);
+ }
+
+ if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) {
+ TRANSPORT_LOGE("unexpected event. fd %d", evt[i].data.fd);
+ } else {
+ {
+ utils::SpinLock::Acquire locked(event_callback_map_lock_);
+ callback = event_callback_map_[evt[i].data.fd];
+ }
+
+ callback(evt[i]);
+
+ // In the callback the epoll event reactor could have been stopped,
+ // then we need to check whether the event loop is still running.
+ if (TRANSPORT_EXPECT_FALSE(!run_event_loop_)) {
+ return;
+ }
+ }
+ } else {
+ TRANSPORT_LOGE("unexpected event. fd %d", evt[i].data.fd);
+ }
+ }
+ }
+}
+
+void EpollEventReactor::runOneEvent() {
+ Event evt;
+ int en = 0;
+ EventCallbackMap::iterator it;
+ EventCallback callback;
+
+ // evt.events = EPOLLIN | EPOLLOUT;
+ sigset_t sigset;
+ sigemptyset(&sigset);
+
+ memset(&evt, 0, sizeof(evt));
+
+ en = epoll_pwait(epoll_fd_, &evt, 1, -1, &sigset);
+
+ if (TRANSPORT_EXPECT_FALSE(en < 0)) {
+ TRANSPORT_LOGE("epoll_pwait: %s", strerror(errno));
+ return;
+ }
+
+ if (TRANSPORT_EXPECT_TRUE(evt.data.fd > 0)) {
+ {
+ utils::SpinLock::Acquire locked(event_callback_map_lock_);
+ it = event_callback_map_.find(evt.data.fd);
+ }
+
+ if (TRANSPORT_EXPECT_FALSE(it == event_callback_map_.end())) {
+ TRANSPORT_LOGE("unexpected event. fd %d", evt.data.fd);
+ } else {
+ {
+ utils::SpinLock::Acquire locked(event_callback_map_lock_);
+ callback = event_callback_map_[evt.data.fd];
+ }
+
+ callback(evt);
+ }
+ } else {
+ TRANSPORT_LOGE("unexpected event. fd %d", evt.data.fd);
+ }
+}
+
+void EpollEventReactor::stop() { run_event_loop_ = false; }
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/utils/epoll_event_reactor.h b/libtransport/src/utils/epoll_event_reactor.h
new file mode 100644
index 000000000..4cb87ebd4
--- /dev/null
+++ b/libtransport/src/utils/epoll_event_reactor.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/utils/spinlock.h>
+#include <utils/event_reactor.h>
+
+#include <sys/epoll.h>
+#include <atomic>
+#include <cstddef>
+#include <functional>
+#include <mutex>
+#include <unordered_map>
+
+#define FD_NUMBER 20000
+
+namespace utils {
+
+typedef struct epoll_event Event;
+typedef std::function<int(const Event &)> EventCallback;
+typedef std::unordered_map<int, EventCallback> EventCallbackMap;
+
+class EpollEventReactor : public EventReactor {
+ public:
+ explicit EpollEventReactor();
+
+ ~EpollEventReactor();
+
+ template <typename EventHandler>
+ int addFileDescriptor(int fd, uint32_t events, EventHandler &&callback) {
+ auto it = event_callback_map_.find(fd);
+ int ret = 0;
+
+ if (it == event_callback_map_.end()) {
+ {
+ utils::SpinLock::Acquire locked(event_callback_map_lock_);
+ event_callback_map_[fd] = std::forward<EventHandler &&>(callback);
+ }
+
+ ret = addFileDescriptor(fd, events);
+ }
+
+ return ret;
+ }
+
+ int delFileDescriptor(int fd);
+
+ int modFileDescriptor(int fd, uint32_t events);
+
+ void runEventLoop(int timeout = -1) override;
+
+ void runOneEvent() override;
+
+ void stop() override;
+
+ std::size_t mapSize();
+
+ private:
+ int addFileDescriptor(int fd, uint32_t events);
+
+ int epoll_fd_;
+ std::atomic_bool run_event_loop_;
+ EventCallbackMap event_callback_map_;
+ utils::SpinLock event_callback_map_lock_;
+};
+
+} // namespace utils
diff --git a/libtransport/src/utils/event_reactor.h b/libtransport/src/utils/event_reactor.h
new file mode 100644
index 000000000..4f8b58296
--- /dev/null
+++ b/libtransport/src/utils/event_reactor.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <functional>
+#include <system_error>
+
+namespace utils {
+
+typedef std::function<void(const std::error_code &ec)> UserCallback;
+
+class EventReactor {
+ public:
+ virtual ~EventReactor() = default;
+
+ virtual void runEventLoop(int timeout = -1) = 0;
+
+ virtual void runOneEvent() = 0;
+
+ virtual void stop() = 0;
+};
+
+} // namespace utils
diff --git a/libtransport/src/utils/event_thread.h b/libtransport/src/utils/event_thread.h
new file mode 100644
index 000000000..e50ae9648
--- /dev/null
+++ b/libtransport/src/utils/event_thread.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/errors/runtime_exception.h>
+#include <memory>
+
+#include <asio.hpp>
+
+namespace utils {
+
+class EventThread {
+ private:
+ // No copies
+ EventThread(const EventThread&) = delete; // non construction-copyable
+ EventThread& operator=(const EventThread&) = delete; // non copyable
+
+ public:
+ explicit EventThread(asio::io_service& io_service)
+ : internal_io_service_(nullptr),
+ io_service_(io_service),
+ work_(io_service_),
+ thread_(nullptr) {
+ run();
+ }
+
+ explicit EventThread()
+ : internal_io_service_(std::make_unique<asio::io_service>()),
+ io_service_(*internal_io_service_),
+ work_(io_service_),
+ thread_(nullptr) {
+ run();
+ }
+
+ ~EventThread() { stop(); }
+
+ void run() {
+ if (stopped()) {
+ io_service_.reset();
+ }
+
+ thread_ = std::make_unique<std::thread>([this]() { io_service_.run(); });
+ }
+
+ std::thread::id getThreadId() const {
+ if (thread_) {
+ return thread_->get_id();
+ } else {
+ throw errors::RuntimeException("Event thread is not running.");
+ }
+ }
+
+ template <typename Func>
+ void add(Func&& f) {
+ // If the function f
+ // TODO USe post in mac os, asio->post in xenial
+ io_service_.post(std::forward<Func&&>(f));
+ }
+
+ template <typename Func>
+ void tryRunHandlerNow(Func&& f) {
+ io_service_.dispatch(std::forward<Func&&>(f));
+ }
+
+ void stop() {
+ io_service_.stop();
+
+ if (thread_ && thread_->joinable()) {
+ thread_->join();
+ }
+
+ thread_.reset();
+ }
+
+ bool stopped() { return io_service_.stopped(); }
+
+ asio::io_service& getIoService() { return io_service_; }
+
+ private:
+ std::unique_ptr<asio::io_service> internal_io_service_;
+ asio::io_service& io_service_;
+ asio::io_service::work work_;
+ std::unique_ptr<std::thread> thread_;
+};
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/utils/fd_deadline_timer.h b/libtransport/src/utils/fd_deadline_timer.h
new file mode 100644
index 000000000..8bc3bbca3
--- /dev/null
+++ b/libtransport/src/utils/fd_deadline_timer.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/utils/log.h>
+
+#include <utils/deadline_timer.h>
+#include <utils/epoll_event_reactor.h>
+
+#include <chrono>
+#include <cstddef>
+
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+namespace utils {
+
+class FdDeadlineTimer : public DeadlineTimer<FdDeadlineTimer> {
+ public:
+ explicit FdDeadlineTimer(EpollEventReactor &reactor)
+ : reactor_(reactor),
+ timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)),
+ flags_(0) {
+ if (timer_fd_ == -1) {
+ throw errors::RuntimeException("Impossible to create the timer!");
+ }
+ }
+
+ ~FdDeadlineTimer() { close(timer_fd_); }
+
+ template <typename WaitHandler>
+ void asyncWaitImpl(WaitHandler &&callback) {
+ // ASIO_WAIT_HANDLER_CHECK(WaitHandler, callback) type_check;
+
+ if (timerfd_settime(timer_fd_, flags_, &new_value_, NULL) == -1) {
+ throw errors::RuntimeException("Impossible to set the timer!");
+ }
+
+ uint32_t events = EPOLLIN;
+
+ reactor_.addFileDescriptor(
+ timer_fd_, events,
+ [callback = std::forward<WaitHandler &&>(callback)](
+ const Event &event) -> int {
+ uint64_t s = 0;
+ std::error_code ec;
+
+ if (read(event.data.fd, &s, sizeof(s)) == -1) {
+ TRANSPORT_LOGE("Read error!!");
+ }
+
+ if (!(event.events & EPOLLIN)) {
+ ec = std::make_error_code(std::errc::operation_canceled);
+ }
+
+ callback(ec);
+
+ return 0;
+ });
+ }
+
+ void waitImpl() {
+ if (timerfd_settime(timer_fd_, flags_, &new_value_, NULL) == -1) {
+ throw errors::RuntimeException("Impossible to set the timer!");
+ }
+
+ uint64_t ret;
+
+ if (read(timer_fd_, &ret, sizeof(ret)) == -1) {
+ throw errors::RuntimeException(
+ "Error while waiting for the timer expiration.");
+ }
+ }
+
+ template <typename T, typename R>
+ void expiresFromNowImpl(std::chrono::duration<T, R> &&duration) {
+ std::memset(&new_value_, 0, sizeof(new_value_));
+ new_value_.it_value = std::chrono::duration_cast<struct timespec>(
+ std::forward<std::chrono::duration<T, R>>(duration));
+ }
+
+ template <typename TimePoint,
+ typename = std::enable_if_t<
+ std::is_same<std::remove_reference_t<TimePoint>,
+ std::chrono::steady_clock::time_point>::value,
+ TimePoint>>
+ void expiresAtImpl(TimePoint &&time_point) {
+ std::memset(&new_value_, 0, sizeof(new_value_));
+
+ new_value_.it_value = std::chrono::duration_cast<struct timespec>(
+ time_point.time_since_epoch());
+ flags_ |= TFD_TIMER_ABSTIME;
+ }
+
+ void cancelImpl() {
+ std::memset(&new_value_, 0, sizeof(new_value_));
+
+ if (timerfd_settime(timer_fd_, 0, &new_value_, NULL) == -1) {
+ throw errors::RuntimeException("Impossible to cancel the timer!");
+ }
+
+ // reactor_.delFileDescriptor(timer_fd_);
+ }
+
+ EventReactor &getEventReactor() { return reactor_; }
+
+ private:
+ EpollEventReactor &reactor_;
+ int timer_fd_;
+ EventCallback callback_;
+ struct itimerspec new_value_;
+ int flags_;
+};
+
+} // namespace utils
diff --git a/libtransport/src/utils/log.cc b/libtransport/src/utils/log.cc
new file mode 100644
index 000000000..27dd3f541
--- /dev/null
+++ b/libtransport/src/utils/log.cc
@@ -0,0 +1,1403 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 wonder-mice
+ *
+ * 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.
+ */
+
+/* When defined, Android log (android/log.h) will be used by default instead of
+ * stderr (ignored on non-Android platforms). Date, time, pid and tid (context)
+ * will be provided by Android log. Android log features will be used to output
+ * log level and tag.
+ */
+
+#if defined(__ANDROID__)
+#define TRANSPORT_LOG_USE_ANDROID_LOG 1
+#define ANDROID_TAG "HicnTransport"
+#else
+#define TRANSPORT_LOG_USE_ANDROID_LOG 0
+#endif
+
+/* When defined, NSLog (uses Apple System Log) will be used instead of stderr
+ * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be
+ * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on
+ * non-public CFLog() function. Both use Apple System Log internally, but it's
+ * easier to call CFLog() from C than NSLog(). Current implementation doesn't
+ * support "%@" format specifier.
+ */
+#ifdef TRANSPORT_LOG_USE_NSLOG
+#undef TRANSPORT_LOG_USE_NSLOG
+#if defined(__APPLE__) && defined(__MACH__)
+#define TRANSPORT_LOG_USE_NSLOG 1
+#else
+#define TRANSPORT_LOG_USE_NSLOG 0
+#endif
+#else
+#define TRANSPORT_LOG_USE_NSLOG 0
+#endif
+/* When defined, OutputDebugString() will be used instead of stderr (ignored on
+ * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with
+ * UTF-8 data.
+ */
+#ifdef TRANSPORT_LOG_USE_DEBUGSTRING
+#undef TRANSPORT_LOG_USE_DEBUGSTRING
+#if defined(_WIN32) || defined(_WIN64)
+#define TRANSPORT_LOG_USE_DEBUGSTRING 1
+#else
+#define TRANSPORT_LOG_USE_DEBUGSTRING 0
+#endif
+#else
+#define TRANSPORT_LOG_USE_DEBUGSTRING 0
+#endif
+/* When defined, TRANSPORT_LOG library will not contain definition of tag prefix
+ * variable. In that case it must be defined elsewhere using
+ * TRANSPORT_LOG_DEFINE_TAG_PREFIX macro, for example:
+ *
+ * TRANSPORT_LOG_DEFINE_TAG_PREFIX = "ProcessName";
+ *
+ * This allows to specify custom value for static initialization and avoid
+ * overhead of setting this value in runtime.
+ */
+#ifdef TRANSPORT_LOG_EXTERN_TAG_PREFIX
+#undef TRANSPORT_LOG_EXTERN_TAG_PREFIX
+#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 1
+#else
+#define TRANSPORT_LOG_EXTERN_TAG_PREFIX 0
+#endif
+/* When defined, TRANSPORT_LOG library will not contain definition of global
+ * format variable. In that case it must be defined elsewhere using
+ * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT macro, for example:
+ *
+ * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH};
+ *
+ * This allows to specify custom value for static initialization and avoid
+ * overhead of setting this value in runtime.
+ */
+#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT
+#undef TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT
+#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 1
+#else
+#define TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT 0
+#endif
+/* When defined, transport_log library will not contain definition of global
+ * output variable. In that case it must be defined elsewhere using
+ * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example:
+ *
+ * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_PUT_STD,
+ * custom_output_callback};
+ *
+ * This allows to specify custom value for static initialization and avoid
+ * overhead of setting this value in runtime.
+ */
+#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT
+#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT
+#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 1
+#else
+#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT 0
+#endif
+/* When defined, transport_log library will not contain definition of global
+ * output level variable. In that case it must be defined elsewhere using
+ * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example:
+ *
+ * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = TRANSPORT_LOG_WARN;
+ *
+ * This allows to specify custom value for static initialization and avoid
+ * overhead of setting this value in runtime.
+ */
+#ifdef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
+#undef TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
+#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1
+#else
+#define TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0
+#endif
+/* When defined, implementation will prefer smaller code size over speed.
+ * Very rough estimate is that code will be up to 2x smaller and up to 2x
+ * slower. Disabled by default.
+ */
+#ifdef TRANSPORT_LOG_OPTIMIZE_SIZE
+#undef TRANSPORT_LOG_OPTIMIZE_SIZE
+#define TRANSPORT_LOG_OPTIMIZE_SIZE 1
+#else
+#define TRANSPORT_LOG_OPTIMIZE_SIZE 0
+#endif
+/* Size of the log line buffer. The buffer is allocated on stack. It limits
+ * maximum length of a log line.
+ */
+#ifndef TRANSPORT_LOG_BUF_SZ
+#define TRANSPORT_LOG_BUF_SZ 512
+#endif
+/* Default number of bytes in one line of memory output. For large values
+ * TRANSPORT_LOG_BUF_SZ also must be increased.
+ */
+#ifndef TRANSPORT_LOG_MEM_WIDTH
+#define TRANSPORT_LOG_MEM_WIDTH 32
+#endif
+/* String to put in the end of each log line (can be empty). Its value used by
+ * stderr output callback. Its size used as a default value for
+ * TRANSPORT_LOG_EOL_SZ.
+ */
+#ifndef TRANSPORT_LOG_EOL
+#define TRANSPORT_LOG_EOL "\n"
+#endif
+/* Default delimiter that separates parts of log message. Can NOT contain '%'
+ * or '\0'.
+ *
+ * Log message format specifications can override (or ignore) this value. For
+ * more details see TRANSPORT_LOG_MESSAGE_CTX_FORMAT,
+ * TRANSPORT_LOG_MESSAGE_SRC_FORMAT and TRANSPORT_LOG_MESSAGE_TAG_FORMAT.
+ */
+#ifndef TRANSPORT_LOG_DEF_DELIMITER
+#define TRANSPORT_LOG_DEF_DELIMITER " "
+#endif
+/* Specifies log message context format. Log message context includes date,
+ * time, process id, thread id and message's log level. Custom information can
+ * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND,
+ * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements),
+ * F_UINT(width, value).
+ *
+ * Must be defined as a tuple, for example:
+ *
+ * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY,
+ * S(" > "))
+ *
+ * In that case, resulting log message will be:
+ *
+ * 2016.12.22 > TAG function@filename.c:line Message text
+ *
+ * Note, that tag, source location and message text are not impacted by
+ * this setting. See TRANSPORT_LOG_MESSAGE_TAG_FORMAT and
+ * TRANSPORT_LOG_MESSAGE_SRC_FORMAT.
+ *
+ * If message context must be visually separated from the rest of the message,
+ * it must be reflected in context format (notice trailing S(" > ") in the
+ * example above).
+ *
+ * S(str) adds constant string str. String can NOT contain '%' or '\0'.
+ *
+ * F_INIT(statements) adds initialization statement(s) that will be evaluated
+ * once for each log message. All statements are evaluated in specified order.
+ * Several F_INIT() fields can be used in every log message format
+ * specification. Fields, like F_UINT(width, value), are allowed to use results
+ * of initialization statements. If statement introduces variables (or other
+ * names, like structures) they must be prefixed with "f_". Statements must be
+ * enclosed into additional "()". Example:
+ *
+ * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \
+ * (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \
+ * YEAR, S("."), MONTH, S("."), DAY, S(" "), \
+ * F_UINT(5, f_ru.ru_nsignals), \
+ * S(" "))
+ *
+ * F_UINT(width, value) adds unsigned integer value extended with up to width
+ * spaces (for alignment purposes). Value can be any expression that evaluates
+ * to unsigned integer. If expression contains non-standard functions, they
+ * must be declared with F_INIT(). Example:
+ *
+ * #define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \
+ * (YEAR, S("."), MONTH, S("."), DAY, S(" "), \
+ * F_INIT(( unsigned tickcount(); )), \
+ * F_UINT(5, tickcount()), \
+ * S(" "))
+ *
+ * Other log message format specifications follow same rules, but have a
+ * different set of supported fields.
+ */
+#ifndef TRANSPORT_LOG_MESSAGE_CTX_FORMAT
+#define TRANSPORT_LOG_MESSAGE_CTX_FORMAT \
+ (MONTH, S("-"), DAY, S(TRANSPORT_LOG_DEF_DELIMITER), HOUR, S(":"), MINUTE, \
+ S(":"), SECOND, S("."), MILLISECOND, S(TRANSPORT_LOG_DEF_DELIMITER), PID, \
+ S(TRANSPORT_LOG_DEF_DELIMITER), TID, S(TRANSPORT_LOG_DEF_DELIMITER), LEVEL, \
+ S(TRANSPORT_LOG_DEF_DELIMITER))
+#endif
+/* Example:
+ */
+/* Specifies log message tag format. It includes tag prefix and tag. Custom
+ * information can be added as well. Supported fields:
+ * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements),
+ * F_UINT(width, value).
+ *
+ * TAG(prefix_delimiter, tag_delimiter) adds following string to log message:
+ *
+ * PREFIX<prefix_delimiter>TAG<tag_delimiter>
+ *
+ * Prefix delimiter will be used only when prefix is not empty. Tag delimiter
+ * will be used only when prefixed tag is not empty. Example:
+ *
+ * #define TRANSPORT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] "))
+ *
+ * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details.
+ */
+#ifndef TRANSPORT_LOG_MESSAGE_TAG_FORMAT
+#define TRANSPORT_LOG_MESSAGE_TAG_FORMAT (TAG(".", TRANSPORT_LOG_DEF_DELIMITER))
+#endif
+/* Specifies log message source location format. It includes function name,
+ * file name and file line. Custom information can be added as well. Supported
+ * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements),
+ * F_UINT(width, value).
+ *
+ * See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details.
+ */
+#ifndef TRANSPORT_LOG_MESSAGE_SRC_FORMAT
+#define TRANSPORT_LOG_MESSAGE_SRC_FORMAT \
+ (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(TRANSPORT_LOG_DEF_DELIMITER))
+#endif
+/* Fields that can be used in log message format specifications (see above).
+ * Mentioning them here explicitly, so we know that nobody else defined them
+ * before us. See TRANSPORT_LOG_MESSAGE_CTX_FORMAT for details.
+ */
+#define YEAR YEAR
+#define MONTH MONTH
+#define DAY DAY
+#define MINUTE MINUTE
+#define SECOND SECOND
+#define MILLISECOND MILLISECOND
+#define PID PID
+#define TID TID
+#define LEVEL LEVEL
+#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim)
+#define FUNCTION FUNCTION
+#define FILENAME FILENAME
+#define FILELINE FILELINE
+#define S(str) S(str)
+#define F_INIT(statements) F_INIT(statements)
+#define F_UINT(width, value) F_UINT(width, value)
+/* Number of bytes to reserve for EOL in the log line buffer (must be >0).
+ * Must be larger than or equal to length of TRANSPORT_LOG_EOL with terminating
+ * null.
+ */
+#ifndef TRANSPORT_LOG_EOL_SZ
+#define TRANSPORT_LOG_EOL_SZ sizeof(TRANSPORT_LOG_EOL)
+#endif
+/* Compile instrumented version of the library to facilitate unit testing.
+ */
+#ifndef TRANSPORT_LOG_INSTRUMENTED
+#define TRANSPORT_LOG_INSTRUMENTED 0
+#endif
+
+#if defined(__linux__)
+#if !defined(__ANDROID__) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+#endif
+#if defined(__MINGW32__)
+#ifdef __STRICT_ANSI__
+#undef __STRICT_ANSI__
+#endif
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <hicn/transport/utils/log.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+#include <windows.h>
+#else
+#include <sys/time.h>
+#include <unistd.h>
+#if defined(__linux__)
+#include <linux/limits.h>
+#else
+#include <sys/syslimits.h>
+#endif
+#endif
+
+#if defined(__linux__)
+#include <sys/prctl.h>
+#include <sys/types.h>
+#if !defined(__ANDROID__)
+#include <sys/syscall.h>
+#endif
+#endif
+#if defined(__MACH__)
+#include <pthread.h>
+#endif
+
+#define INLINE _TRANSPORT_LOG_INLINE
+#define VAR_UNUSED(var) (void)var
+#define RETVAL_UNUSED(expr) \
+ do { \
+ while (expr) break; \
+ } while (0)
+#define STATIC_ASSERT(name, cond) typedef char assert_##name[(cond) ? 1 : -1]
+#define ASSERT_UNREACHABLE(why) assert(!sizeof(why))
+#ifndef _countof
+#define _countof(xs) (sizeof(xs) / sizeof((xs)[0]))
+#endif
+
+#if TRANSPORT_LOG_INSTRUMENTED
+#define INSTRUMENTED_CONST
+#else
+#define INSTRUMENTED_CONST const
+#endif
+
+#define _PP_PASTE_2(a, b) a##b
+#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b)
+
+#define _PP_PASTE_3(a, b, c) a##b##c
+#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c)
+
+/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__
+ * as a single token and requires additional expansion to realize that it's
+ * actually a list. If not for it, there would be no need in this extra
+ * expansion.
+ */
+#define _PP_ID(x) x
+#define _PP_NARGS_N(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
+ _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, \
+ _24, ...) \
+ _24
+#define _PP_NARGS(...) \
+ _PP_ID(_PP_NARGS_N(__VA_ARGS__, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, \
+ 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
+
+/* There is a more efficient way to implement this, but it requires
+ * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't
+ * have one.
+ */
+#define _PP_HEAD__(x, ...) x
+#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~))
+#define _PP_HEAD(xs) _PP_HEAD_ xs
+#define _PP_TAIL_(x, ...) (__VA_ARGS__)
+#define _PP_TAIL(xs) _PP_TAIL_ xs
+#define _PP_UNTUPLE_(...) __VA_ARGS__
+#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs
+
+/* Apply function macro to each element in tuple. Output is not
+ * enforced to be a tuple.
+ */
+#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs))
+#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs))
+#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs))
+#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs))
+#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs))
+#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs))
+#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs))
+#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs))
+#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs))
+#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs))
+#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs))
+#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs))
+#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs))
+#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs))
+#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs))
+#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs))
+#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs))
+#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs))
+#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs))
+#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs))
+#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs))
+#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs))
+#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs))
+#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs))
+#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs)(f, xs)
+
+/* Apply function macro to each element in tuple in reverse order.
+ * Output is not enforced to be a tuple.
+ */
+#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs))
+#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs)(f, xs)
+
+/* Used to implement _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All
+ * possible fields must be mentioned here. Not counting F_INIT() here because
+ * it's somewhat special and is handled spearatly (at least for now).
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__ (0 << 0)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__YEAR (1 << 1)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MONTH (1 << 2)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__DAY (1 << 3)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__HOUR (1 << 4)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1 << 5)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__SECOND (1 << 6)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1 << 7)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__PID (1 << 8)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TID (1 << 9)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1 << 10)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1 << 11)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1 << 12)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1 << 13)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1 << 14)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__S(s) (1 << 15)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0 << 16)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1 << 17)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) \
+ _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_MASK_, _, field)
+
+/* Logical "or" of masks of fields used in specified format specification.
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format) \
+ (0 _PP_MAP(| _TRANSPORT_LOG_MESSAGE_FORMAT_MASK, format))
+
+/* Expands to expressions that evaluates to true if field is used in
+ * specified format specification. Example:
+ *
+ * #if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT,
+ * TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+ * ...
+ * #endif
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \
+ (_TRANSPORT_LOG_MESSAGE_FORMAT_MASK(field) & \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(format))
+
+/* Same, but checks all supported format specifications.
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \
+ (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \
+ TRANSPORT_LOG_MESSAGE_TAG_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(field, \
+ TRANSPORT_LOG_MESSAGE_SRC_FORMAT))
+
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED \
+ (_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT))
+
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
+#pragma warning(disable : 4204) /* nonstandard extension used: non-constant \
+ aggregate initializer */
+#define memccpy _memccpy
+#endif
+
+#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || defined(__MINGW64__)
+#define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va)
+static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap) {
+ const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap);
+ return 0 < n ? n : (int)sz + 1; /* no need in _vscprintf() for now */
+}
+#if TRANSPORT_LOG_OPTIMIZE_SIZE
+#define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__)
+static int fake_snprintf(char *s, size_t sz, const char *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ const int n = fake_vsnprintf(s, sz, fmt, va);
+ va_end(va);
+ return n;
+}
+#endif
+#endif
+
+typedef void (*time_cb)(struct tm *const tm, unsigned *const usec);
+typedef void (*pid_cb)(int *const pid, int *const tid);
+typedef void (*buffer_cb)(transport_log_message *msg, char *buf);
+
+typedef struct src_location {
+ const char *const func;
+ const char *const file;
+ const unsigned line;
+} src_location;
+
+typedef struct mem_block {
+ const void *const d;
+ const unsigned d_sz;
+} mem_block;
+
+static void time_callback(struct tm *const tm, unsigned *const usec);
+static void pid_callback(int *const pid, int *const tid);
+static void buffer_callback(transport_log_message *msg, char *buf);
+
+STATIC_ASSERT(eol_fits_eol_sz,
+ sizeof(TRANSPORT_LOG_EOL) <= TRANSPORT_LOG_EOL_SZ);
+STATIC_ASSERT(eol_sz_greater_than_zero, 0 < TRANSPORT_LOG_EOL_SZ);
+STATIC_ASSERT(eol_sz_less_than_buf_sz,
+ TRANSPORT_LOG_EOL_SZ < TRANSPORT_LOG_BUF_SZ);
+#if !defined(_WIN32) && !defined(_WIN64)
+STATIC_ASSERT(buf_sz_less_than_pipe_buf, TRANSPORT_LOG_BUF_SZ <= PIPE_BUF);
+#endif
+static const char c_hex[] = "0123456789abcdef";
+
+static INSTRUMENTED_CONST unsigned g_buf_sz =
+ TRANSPORT_LOG_BUF_SZ - TRANSPORT_LOG_EOL_SZ;
+static INSTRUMENTED_CONST time_cb g_time_cb = time_callback;
+static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback;
+static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback;
+
+#if TRANSPORT_LOG_USE_ANDROID_LOG
+#include <android/log.h>
+
+static INLINE int android_lvl(const int lvl) {
+ switch (lvl) {
+ case TRANSPORT_LOG_VERBOSE:
+ return ANDROID_LOG_VERBOSE;
+ case TRANSPORT_LOG_DEBUG:
+ return ANDROID_LOG_DEBUG;
+ case TRANSPORT_LOG_INFO:
+ return ANDROID_LOG_INFO;
+ case TRANSPORT_LOG_WARN:
+ return ANDROID_LOG_WARN;
+ case TRANSPORT_LOG_ERROR:
+ return ANDROID_LOG_ERROR;
+ case TRANSPORT_LOG_FATAL:
+ return ANDROID_LOG_FATAL;
+ default:
+ ASSERT_UNREACHABLE("Bad log level");
+ return ANDROID_LOG_UNKNOWN;
+ }
+}
+
+static void out_android_callback(const transport_log_message *const msg,
+ void *arg) {
+ VAR_UNUSED(arg);
+ *msg->p = 0;
+ const char *tag = msg->p;
+ if (msg->tag_e != msg->tag_b) {
+ tag = msg->tag_b;
+ *msg->tag_e = 0;
+ }
+ __android_log_print(android_lvl(msg->lvl), ANDROID_TAG, "%s", msg->msg_b);
+}
+
+enum { OUT_ANDROID_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX };
+#define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback
+#endif
+
+#if TRANSPORT_LOG_USE_NSLOG
+#include <CoreFoundation/CoreFoundation.h>
+CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...);
+
+static INLINE int apple_lvl(const int lvl) {
+ switch (lvl) {
+ case TRANSPORT_LOG_VERBOSE:
+ return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */
+ ;
+ case TRANSPORT_LOG_DEBUG:
+ return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */
+ ;
+ case TRANSPORT_LOG_INFO:
+ return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */
+ ;
+ case TRANSPORT_LOG_WARN:
+ return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */
+ ;
+ case TRANSPORT_LOG_ERROR:
+ return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */
+ ;
+ case TRANSPORT_LOG_FATAL:
+ return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */
+ ;
+ default:
+ ASSERT_UNREACHABLE("Bad log level");
+ return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */
+ ;
+ }
+}
+
+static void out_nslog_callback(const transport_log_message *const msg,
+ void *arg) {
+ VAR_UNUSED(arg);
+ *msg->p = 0;
+ CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b);
+}
+
+enum { OUT_NSLOG_MASK = TRANSPORT_LOG_PUT_STD & ~TRANSPORT_LOG_PUT_CTX };
+#define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback
+#endif
+
+#if TRANSPORT_LOG_USE_DEBUGSTRING
+#include <windows.h>
+
+static void out_debugstring_callback(const transport_log_message *const msg,
+ void *arg) {
+ VAR_UNUSED(arg);
+ msg->p[0] = '\n';
+ msg->p[1] = '\0';
+ OutputDebugStringA(msg->buf);
+}
+
+enum { OUT_DEBUGSTRING_MASK = TRANSPORT_LOG_PUT_STD };
+#define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback
+#endif
+
+void transport_log_out_stderr_callback(const transport_log_message *const msg,
+ void *arg) {
+ VAR_UNUSED(arg);
+ const size_t eol_len = sizeof(TRANSPORT_LOG_EOL) - 1;
+ memcpy(msg->p, TRANSPORT_LOG_EOL, eol_len);
+#if defined(_WIN32) || defined(_WIN64)
+ /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and
+ without FILE_WRITE_DATA */
+ DWORD written;
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf,
+ (DWORD)(msg->p - msg->buf + eol_len), &written, 0);
+#else
+ /* write() is atomic for buffers less than or equal to PIPE_BUF. */
+ RETVAL_UNUSED(
+ write(STDERR_FILENO, msg->buf, (size_t)(msg->p - msg->buf) + eol_len));
+#endif
+}
+
+static const transport_log_output out_stderr = {TRANSPORT_LOG_OUT_STDERR};
+
+#if !TRANSPORT_LOG_EXTERN_TAG_PREFIX
+TRANSPORT_LOG_DEFINE_TAG_PREFIX = 0;
+#endif
+
+#if !TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT
+TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {TRANSPORT_LOG_MEM_WIDTH};
+#endif
+
+#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT
+#if TRANSPORT_LOG_USE_ANDROID_LOG
+TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID};
+#elif TRANSPORT_LOG_USE_NSLOG
+TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG};
+#elif TRANSPORT_LOG_USE_DEBUGSTRING
+TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING};
+#else
+TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_OUT_STDERR};
+#endif
+#endif
+
+#if !TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
+TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0;
+#endif
+
+const transport_log_spec _transport_log_stderr_spec = {
+ TRANSPORT_LOG_GLOBAL_FORMAT,
+ &out_stderr,
+};
+
+static const transport_log_spec global_spec = {
+ TRANSPORT_LOG_GLOBAL_FORMAT,
+ TRANSPORT_LOG_GLOBAL_OUTPUT,
+};
+
+#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+static char lvl_char(const int lvl) {
+ switch (lvl) {
+ case TRANSPORT_LOG_VERBOSE:
+ return 'V';
+ case TRANSPORT_LOG_DEBUG:
+ return 'D';
+ case TRANSPORT_LOG_INFO:
+ return 'I';
+ case TRANSPORT_LOG_WARN:
+ return 'W';
+ case TRANSPORT_LOG_ERROR:
+ return 'E';
+ case TRANSPORT_LOG_FATAL:
+ return 'F';
+ default:
+ ASSERT_UNREACHABLE("Bad log level");
+ return '?';
+ }
+}
+#endif
+
+#define GCCVER_LESS(MAJOR, MINOR, PATCH) \
+ (__GNUC__ < MAJOR || (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \
+ (__GNUC_MINOR__ == MINOR && \
+ __GNUC_PATCHLEVEL__ < PATCH))))
+
+#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4, 7, 0)
+#define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0)
+#define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n)
+#define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n)
+#define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n)
+#define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n)
+/* Note: will not store old value of *vp in *ep (non-standard behaviour) */
+#define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \
+ __sync_bool_compare_and_swap(vp, *(ep), d)
+#endif
+
+#if !TRANSPORT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64)
+#define TCACHE
+#define TCACHE_STALE (0x40000000)
+#define TCACHE_FLUID (0x40000000 | 0x80000000)
+static unsigned g_tcache_mode = TCACHE_STALE;
+static struct timeval g_tcache_tv = {0, 0};
+static struct tm g_tcache_tm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static INLINE int tcache_get(const struct timeval *const tv,
+ struct tm *const tm) {
+ unsigned mode;
+ mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED);
+ if (0 == (mode & TCACHE_FLUID)) {
+ mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE);
+ if (0 == (mode & TCACHE_FLUID)) {
+ if (g_tcache_tv.tv_sec == tv->tv_sec) {
+ *tm = g_tcache_tm;
+ __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
+ return !0;
+ }
+ __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED);
+ }
+ __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
+ }
+ return 0;
+}
+
+static INLINE void tcache_set(const struct timeval *const tv,
+ struct tm *const tm) {
+ unsigned stale = TCACHE_STALE;
+ if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID, 0,
+ __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
+ g_tcache_tv = *tv;
+ g_tcache_tm = *tm;
+ __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE);
+ }
+}
+#endif
+
+static void time_callback(struct tm *const tm, unsigned *const msec) {
+#if !_TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED
+ VAR_UNUSED(tm);
+ VAR_UNUSED(msec);
+#else
+#if defined(_WIN32) || defined(_WIN64)
+ SYSTEMTIME st;
+ GetLocalTime(&st);
+ tm->tm_year = st.wYear;
+ tm->tm_mon = st.wMonth - 1;
+ tm->tm_mday = st.wDay;
+ tm->tm_wday = st.wDayOfWeek;
+ tm->tm_hour = st.wHour;
+ tm->tm_min = st.wMinute;
+ tm->tm_sec = st.wSecond;
+ *msec = st.wMilliseconds;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+#ifndef TCACHE
+ localtime_r(&tv.tv_sec, tm);
+#else
+ if (!tcache_get(&tv, tm)) {
+ localtime_r(&tv.tv_sec, tm);
+ tcache_set(&tv, tm);
+ }
+#endif
+ *msec = (unsigned)tv.tv_usec / 1000;
+#endif
+#endif
+}
+
+static void pid_callback(int *const pid, int *const tid) {
+#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(PID, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+ VAR_UNUSED(pid);
+#else
+#if defined(_WIN32) || defined(_WIN64)
+ *pid = GetCurrentProcessId();
+#else
+ *pid = getpid();
+#endif
+#endif
+
+#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+ VAR_UNUSED(tid);
+#else
+#if defined(_WIN32) || defined(_WIN64)
+ *tid = GetCurrentThreadId();
+#elif defined(__ANDROID__)
+ *tid = gettid();
+#elif defined(__linux__)
+ *tid = syscall(SYS_gettid);
+#elif defined(__MACH__)
+ *tid = (int)pthread_mach_thread_np(pthread_self());
+#else
+#define Platform not supported
+#endif
+#endif
+}
+
+static void buffer_callback(transport_log_message *msg, char *buf) {
+ msg->e = (msg->p = msg->buf = buf) + g_buf_sz;
+}
+
+#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, \
+ TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
+static const char *funcname(const char *func) { return func ? func : ""; }
+#endif
+
+#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, \
+ TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
+static const char *filename(const char *file) {
+ const char *f = file;
+ for (const char *p = file; 0 != *p; ++p) {
+ if ('/' == *p || '\\' == *p) {
+ f = p + 1;
+ }
+ }
+ return f;
+}
+#endif
+
+static INLINE size_t nprintf_size(transport_log_message *const msg) {
+ // *nprintf() always puts 0 in the end when input buffer is not empty. This
+ // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which
+ // leaves space for one more character. Some put_xxx() functions don't use
+ // *nprintf() and could use that last character. In that case log line will
+ // have multiple (two) half-written parts which is confusing. To workaround
+ // that we allow *nprintf() to write its 0 in the eol area (which is always
+ // not empty).
+ return (size_t)(msg->e - msg->p + 1);
+}
+
+static INLINE void put_nprintf(transport_log_message *const msg, const int n) {
+ if (0 < n) {
+ msg->p = n < msg->e - msg->p ? msg->p + n : msg->e;
+ }
+}
+
+static INLINE char *put_padding_r(const unsigned w, const char wc, char *p,
+ char *e) {
+ for (char *const b = e - w; b < p; *--p = wc) {
+ }
+ return p;
+}
+
+static char *put_integer_r(unsigned v, const int sign, const unsigned w,
+ const char wc, char *const e) {
+ static const char _signs[] = {'-', '0', '+'};
+ static const char *const signs = _signs + 1;
+ char *p = e;
+ do {
+ *--p = '0' + v % 10;
+ } while (0 != (v /= 10));
+ if (0 == sign) return put_padding_r(w, wc, p, e);
+ if ('0' != wc) {
+ *--p = signs[sign];
+ return put_padding_r(w, wc, p, e);
+ }
+ p = put_padding_r(w, wc, p, e + 1);
+ *--p = signs[sign];
+ return p;
+}
+
+static INLINE char *put_uint_r(const unsigned v, const unsigned w,
+ const char wc, char *const e) {
+ return put_integer_r(v, 0, w, wc, e);
+}
+
+static INLINE char *put_int_r(const int v, const unsigned w, const char wc,
+ char *const e) {
+ return 0 <= v ? put_integer_r((unsigned)v, 0, w, wc, e)
+ : put_integer_r((unsigned)-v, -1, w, wc, e);
+}
+
+static INLINE char *put_stringn(const char *const s_p, const char *const s_e,
+ char *const p, char *const e) {
+ const ptrdiff_t m = e - p;
+ ptrdiff_t n = s_e - s_p;
+ if (n > m) {
+ n = m;
+ }
+ memcpy(p, s_p, n);
+ return p + n;
+}
+
+static INLINE char *put_string(const char *s, char *p, char *const e) {
+ const ptrdiff_t n = e - p;
+ char *const c = (char *)memccpy(p, s, '\0', n);
+ return 0 != c ? c - 1 : e;
+}
+
+static INLINE char *put_uint(unsigned v, const unsigned w, const char wc,
+ char *const p, char *const e) {
+ char buf[16];
+ char *const se = buf + _countof(buf);
+ char *sp = put_uint_r(v, w, wc, se);
+ return put_stringn(sp, se, p, e);
+}
+
+#define PUT_CSTR_R(p, STR) \
+ do { \
+ for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \
+ *--(p) = (STR)[i]; \
+ } \
+ } \
+ _TRANSPORT_LOG_ONCE
+
+#define PUT_CSTR_CHECKED(p, e, STR) \
+ do { \
+ for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \
+ *(p)++ = (STR)[i]; \
+ } \
+ } \
+ _TRANSPORT_LOG_ONCE
+
+/* F_INIT field support.
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__YEAR
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MONTH
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__DAY
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__HOUR
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MINUTE
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__SECOND
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__PID
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TID
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__LEVEL
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FUNCTION
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILENAME
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__FILELINE
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__S(s)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_INIT(field) \
+ _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT_, _, field)
+
+/* Implements generation of printf-like format string for log message
+ * format specification.
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ ""
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) ""
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u"
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \
+ _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field)
+
+/* Implements generation of printf-like format parameters for log message
+ * format specification.
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR \
+ , (unsigned)(tm.tm_year + 1900)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH \
+ , (unsigned)(tm.tm_mon + 1)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY , (unsigned)tm.tm_mday
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR , (unsigned)tm.tm_hour
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE , (unsigned)tm.tm_min
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND , (unsigned)tm.tm_sec
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND , (unsigned)msec
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID , pid
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID , tid
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL \
+ , (char)lvl_char(msg->lvl)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION , funcname(src->func)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME , filename(src->file)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE , src->line
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) , v
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \
+ _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field)
+
+/* Implements generation of put_xxx_t statements for log message specification.
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__YEAR \
+ p = put_uint_r(tm.tm_year + 1900, 4, '0', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MONTH \
+ p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__DAY \
+ p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__HOUR \
+ p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE \
+ p = put_uint_r((unsigned)tm.tm_min, 2, '0', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__SECOND \
+ p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND \
+ p = put_uint_r(msec, 3, '0', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) \
+ p = put_uint_r(v, w, ' ', p);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R(field) \
+ _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R_, _, field)
+
+static void put_ctx(transport_log_message *const msg) {
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+ VAR_UNUSED(msg);
+#else
+#if _TRANSPORT_LOG_MESSAGE_FORMAT_DATETIME_USED
+ struct tm tm;
+ unsigned msec;
+ g_time_cb(&tm, &msec);
+#endif
+#if _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \
+ PID, TRANSPORT_LOG_MESSAGE_CTX_FORMAT) || \
+ _TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TID, \
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+ int pid, tid;
+ g_pid_cb(&pid, &tid);
+#endif
+
+#if TRANSPORT_LOG_OPTIMIZE_SIZE
+ int n;
+ n = snprintf(msg->p, nprintf_size(msg),
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT,
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL,
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT));
+ put_nprintf(msg, n);
+#else
+ char buf[64];
+ char *const e = buf + sizeof(buf);
+ char *p = e;
+ _PP_RMAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_R,
+ TRANSPORT_LOG_MESSAGE_CTX_FORMAT)
+ msg->p = put_stringn(p, e, msg->p, msg->e);
+#endif
+#endif
+}
+
+#define PUT_TAG(msg, tag, prefix_delim, tag_delim) \
+ do { \
+ const char *ch; \
+ msg->tag_b = msg->p; \
+ if (0 != (ch = _transport_log_tag_prefix)) { \
+ for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \
+ } \
+ } \
+ if (0 != (ch = tag) && 0 != tag[0]) { \
+ if (msg->tag_b != msg->p) { \
+ PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \
+ } \
+ for (; msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) { \
+ } \
+ } \
+ msg->tag_e = msg->p; \
+ if (msg->tag_b != msg->p) { \
+ PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \
+ } \
+ } \
+ _TRANSPORT_LOG_ONCE
+
+/* Implements simple put statements for log message specification.
+ */
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) \
+ PUT_TAG(msg, tag, pd, td);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FUNCTION \
+ msg->p = put_string(funcname(src->func), msg->p, msg->e);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILENAME \
+ msg->p = put_string(filename(src->file), msg->p, msg->e);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__FILELINE \
+ msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__S(s) \
+ PUT_CSTR_CHECKED(msg->p, msg->e, s);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr)
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) \
+ msg->p = put_uint(v, w, ' ', msg->p, msg->e);
+#define _TRANSPORT_LOG_MESSAGE_FORMAT_PUT(field) \
+ _PP_CONCAT_3(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT_, _, field)
+
+static void put_tag(transport_log_message *const msg, const char *const tag) {
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT)
+#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, \
+ TRANSPORT_LOG_MESSAGE_TAG_FORMAT)
+ VAR_UNUSED(tag);
+#endif
+#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_TAG_FORMAT)
+ VAR_UNUSED(msg);
+#else
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_TAG_FORMAT)
+#endif
+}
+
+static void put_src(transport_log_message *const msg,
+ const src_location *const src) {
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_INIT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
+#if !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \
+ FUNCTION, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \
+ !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS( \
+ FILENAME, TRANSPORT_LOG_MESSAGE_SRC_FORMAT) && \
+ !_TRANSPORT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, \
+ TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
+ VAR_UNUSED(src);
+#endif
+#if !_TRANSPORT_LOG_MESSAGE_FORMAT_FIELDS(TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
+ VAR_UNUSED(msg);
+#else
+#if TRANSPORT_LOG_OPTIMIZE_SIZE
+ int n;
+ n = snprintf(msg->p, nprintf_size(msg),
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_FMT,
+ TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PRINTF_VAL,
+ TRANSPORT_LOG_MESSAGE_SRC_FORMAT));
+ put_nprintf(msg, n);
+#else
+ _PP_MAP(_TRANSPORT_LOG_MESSAGE_FORMAT_PUT, TRANSPORT_LOG_MESSAGE_SRC_FORMAT)
+#endif
+#endif
+}
+
+static void put_msg(transport_log_message *const msg, const char *const fmt,
+ va_list va) {
+ int n;
+ msg->msg_b = msg->p;
+ n = vsnprintf(msg->p, nprintf_size(msg), fmt, va);
+ put_nprintf(msg, n);
+}
+
+static void output_mem(const transport_log_spec *log,
+ transport_log_message *const msg,
+ const mem_block *const mem) {
+ if (0 == mem->d || 0 == mem->d_sz) {
+ return;
+ }
+ const unsigned char *mem_p = (const unsigned char *)mem->d;
+ const unsigned char *const mem_e = mem_p + mem->d_sz;
+ const unsigned char *mem_cut;
+ const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width;
+ char *const hex_b = msg->msg_b;
+ char *const ascii_b = hex_b + 2 * mem_width + 2;
+ char *const ascii_e = ascii_b + mem_width;
+ if (msg->e < ascii_e) {
+ return;
+ }
+ while (mem_p != mem_e) {
+ char *hex = hex_b;
+ char *ascii = ascii_b;
+ for (mem_cut = mem_width < mem_e - mem_p ? mem_p + mem_width : mem_e;
+ mem_cut != mem_p; ++mem_p) {
+ const unsigned char ch = *mem_p;
+ *hex++ = c_hex[(0xf0 & ch) >> 4];
+ *hex++ = c_hex[(0x0f & ch)];
+ *ascii++ = isprint(ch) ? (char)ch : '?';
+ }
+ while (hex != ascii_b) {
+ *hex++ = ' ';
+ }
+ msg->p = ascii;
+ log->output->callback(msg, log->output->arg);
+ }
+}
+
+void transport_log_set_tag_prefix(const char *const prefix) {
+ _transport_log_tag_prefix = prefix;
+}
+
+void transport_log_set_mem_width(const unsigned w) {
+ _transport_log_global_format.mem_width = w;
+}
+
+void transport_log_set_output_level(const int lvl) {
+ _transport_log_global_output_lvl = lvl;
+}
+
+void transport_log_set_output_v(const unsigned mask, void *const arg,
+ const transport_log_output_cb callback) {
+ _transport_log_global_output.mask = mask;
+ _transport_log_global_output.arg = arg;
+ _transport_log_global_output.callback = callback;
+}
+
+static void _transport_log_write_imp(const transport_log_spec *log,
+ const src_location *const src,
+ const mem_block *const mem, const int lvl,
+ const char *const tag,
+ const char *const fmt, va_list va) {
+ transport_log_message msg;
+ char buf[TRANSPORT_LOG_BUF_SZ];
+ const unsigned mask = log->output->mask;
+ msg.lvl = lvl;
+ msg.tag = tag;
+ g_buffer_cb(&msg, buf);
+ if (TRANSPORT_LOG_PUT_CTX & mask) {
+ put_ctx(&msg);
+ }
+ if (TRANSPORT_LOG_PUT_TAG & mask) {
+ put_tag(&msg, tag);
+ }
+ if (0 != src && TRANSPORT_LOG_PUT_SRC & mask) {
+ put_src(&msg, src);
+ }
+ if (TRANSPORT_LOG_PUT_MSG & mask) {
+ put_msg(&msg, fmt, va);
+ }
+ log->output->callback(&msg, log->output->arg);
+ if (0 != mem && TRANSPORT_LOG_PUT_MSG & mask) {
+ output_mem(log, &msg, mem);
+ }
+}
+
+void _transport_log_write_d(const char *const func, const char *const file,
+ const unsigned line, const int lvl,
+ const char *const tag, const char *const fmt, ...) {
+ const src_location src = {func, file, line};
+ va_list va;
+ va_start(va, fmt);
+ _transport_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va);
+ va_end(va);
+}
+
+void _transport_log_write_aux_d(const char *const func, const char *const file,
+ const unsigned line,
+ const transport_log_spec *const log,
+ const int lvl, const char *const tag,
+ const char *const fmt, ...) {
+ const src_location src = {func, file, line};
+ va_list va;
+ va_start(va, fmt);
+ _transport_log_write_imp(log, &src, 0, lvl, tag, fmt, va);
+ va_end(va);
+}
+
+void _transport_log_write(const int lvl, const char *const tag,
+ const char *const fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ _transport_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va);
+ va_end(va);
+}
+
+void _transport_log_write_aux(const transport_log_spec *const log,
+ const int lvl, const char *const tag,
+ const char *const fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ _transport_log_write_imp(log, 0, 0, lvl, tag, fmt, va);
+ va_end(va);
+}
+
+void _transport_log_write_mem_d(const char *const func, const char *const file,
+ const unsigned line, const int lvl,
+ const char *const tag, const void *const d,
+ const unsigned d_sz, const char *const fmt,
+ ...) {
+ const src_location src = {func, file, line};
+ const mem_block mem = {d, d_sz};
+ va_list va;
+ va_start(va, fmt);
+ _transport_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va);
+ va_end(va);
+}
+
+void _transport_log_write_mem_aux_d(const char *const func,
+ const char *const file, const unsigned line,
+ const transport_log_spec *const log,
+ const int lvl, const char *const tag,
+ const void *const d, const unsigned d_sz,
+ const char *const fmt, ...) {
+ const src_location src = {func, file, line};
+ const mem_block mem = {d, d_sz};
+ va_list va;
+ va_start(va, fmt);
+ _transport_log_write_imp(log, &src, &mem, lvl, tag, fmt, va);
+ va_end(va);
+}
+
+void _transport_log_write_mem(const int lvl, const char *const tag,
+ const void *const d, const unsigned d_sz,
+ const char *const fmt, ...) {
+ const mem_block mem = {d, d_sz};
+ va_list va;
+ va_start(va, fmt);
+ _transport_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va);
+ va_end(va);
+}
+
+void _transport_log_write_mem_aux(const transport_log_spec *const log,
+ const int lvl, const char *const tag,
+ const void *const d, const unsigned d_sz,
+ const char *const fmt, ...) {
+ const mem_block mem = {d, d_sz};
+ va_list va;
+ va_start(va, fmt);
+ _transport_log_write_imp(log, 0, &mem, lvl, tag, fmt, va);
+ va_end(va);
+} \ No newline at end of file
diff --git a/libtransport/src/utils/membuf.cc b/libtransport/src/utils/membuf.cc
new file mode 100644
index 000000000..e75e85b35
--- /dev/null
+++ b/libtransport/src/utils/membuf.cc
@@ -0,0 +1,867 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright 2013-present 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.
+ */
+
+/*
+ * The code in this file if adapated from the IOBuf of folly:
+ * https://github.com/facebook/folly/blob/master/folly/io/IOBuf.h
+ */
+#ifdef _WIN32
+#include <hicn/transport/portability/win_portability.h>
+#endif
+
+#include <hicn/transport/utils/membuf.h>
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <stdexcept>
+#include <vector>
+
+using std::unique_ptr;
+
+namespace {
+
+enum : uint16_t {
+ kHeapMagic = 0xa5a5,
+ // This memory segment contains an MemBuf that is still in use
+ kMemBufInUse = 0x01,
+ // This memory segment contains buffer data that is still in use
+ kDataInUse = 0x02,
+};
+
+enum : std::size_t {
+ // When create() is called for buffers less than kDefaultCombinedBufSize,
+ // we allocate a single combined memory segment for the MemBuf and the data
+ // together. See the comments for createCombined()/createSeparate() for more
+ // details.
+ //
+ // (The size of 1k is largely just a guess here. We could could probably do
+ // benchmarks of real applications to see if adjusting this number makes a
+ // difference. Callers that know their exact use case can also explicitly
+ // call createCombined() or createSeparate().)
+ kDefaultCombinedBufSize = 1024
+};
+
+// Helper function for MemBuf::takeOwnership()
+void takeOwnershipError(bool freeOnError, void* buf,
+ utils::MemBuf::FreeFunction freeFn, void* userData) {
+ if (!freeOnError) {
+ return;
+ }
+ if (!freeFn) {
+ free(buf);
+ return;
+ }
+ try {
+ freeFn(buf, userData);
+ } catch (...) {
+ // The user's free function is not allowed to throw.
+ // (We are already in the middle of throwing an exception, so
+ // we cannot let this exception go unhandled.)
+ abort();
+ }
+}
+
+} // namespace
+
+namespace utils {
+
+struct MemBuf::HeapPrefix {
+ explicit HeapPrefix(uint16_t flg) : magic(kHeapMagic), flags(flg) {}
+ ~HeapPrefix() {
+ // Reset magic to 0 on destruction. This is solely for debugging purposes
+ // to help catch bugs where someone tries to use HeapStorage after it has
+ // been deleted.
+ magic = 0;
+ }
+
+ uint16_t magic;
+ std::atomic<uint16_t> flags;
+};
+
+struct MemBuf::HeapStorage {
+ HeapPrefix prefix;
+ // The MemBuf is last in the HeapStorage object.
+ // This way operator new will work even if allocating a subclass of MemBuf
+ // that requires more space.
+ utils::MemBuf buf;
+};
+
+struct MemBuf::HeapFullStorage {
+ // Make sure jemalloc allocates from the 64-byte class. Putting this here
+ // because HeapStorage is private so it can't be at namespace level.
+ static_assert(sizeof(HeapStorage) <= 64,
+ "MemBuf may not grow over 56 bytes!");
+
+ HeapStorage hs;
+ SharedInfo shared;
+ std::max_align_t align;
+};
+
+MemBuf::SharedInfo::SharedInfo() : freeFn(nullptr), userData(nullptr) {
+ // Use relaxed memory ordering here. Since we are creating a new SharedInfo,
+ // no other threads should be referring to it yet.
+ refcount.store(1, std::memory_order_relaxed);
+}
+
+MemBuf::SharedInfo::SharedInfo(FreeFunction fn, void* arg)
+ : freeFn(fn), userData(arg) {
+ // Use relaxed memory ordering here. Since we are creating a new SharedInfo,
+ // no other threads should be referring to it yet.
+ refcount.store(1, std::memory_order_relaxed);
+}
+
+void* MemBuf::operator new(size_t size) {
+ size_t fullSize = offsetof(HeapStorage, buf) + size;
+ auto* storage = static_cast<HeapStorage*>(malloc(fullSize));
+
+ new (&storage->prefix) HeapPrefix(kMemBufInUse);
+ return &(storage->buf);
+}
+
+void* MemBuf::operator new(size_t /* size */, void* ptr) { return ptr; }
+
+void MemBuf::operator delete(void* ptr) {
+ auto* storageAddr = static_cast<uint8_t*>(ptr) - offsetof(HeapStorage, buf);
+ auto* storage = reinterpret_cast<HeapStorage*>(storageAddr);
+ releaseStorage(storage, kMemBufInUse);
+}
+
+void MemBuf::operator delete(void* /* ptr */, void* /* placement */) {
+ // Provide matching operator for `MemBuf::new` to avoid MSVC compilation
+ // warning (C4291) about memory leak when exception is thrown in the
+ // constructor.
+}
+
+void MemBuf::releaseStorage(HeapStorage* storage, uint16_t freeFlags) {
+ // Use relaxed memory order here. If we are unlucky and happen to get
+ // out-of-date data the compare_exchange_weak() call below will catch
+ // it and load new data with memory_order_acq_rel.
+ auto flags = storage->prefix.flags.load(std::memory_order_acquire);
+
+ while (true) {
+ uint16_t newFlags = uint16_t(flags & ~freeFlags);
+ if (newFlags == 0) {
+ // The storage space is now unused. Free it.
+ storage->prefix.HeapPrefix::~HeapPrefix();
+ free(storage);
+ return;
+ }
+
+ // This storage segment still contains portions that are in use.
+ // Just clear the flags specified in freeFlags for now.
+ auto ret = storage->prefix.flags.compare_exchange_weak(
+ flags, newFlags, std::memory_order_acq_rel);
+ if (ret) {
+ // We successfully updated the flags.
+ return;
+ }
+
+ // We failed to update the flags. Some other thread probably updated them
+ // and cleared some of the other bits. Continue around the loop to see if
+ // we are the last user now, or if we need to try updating the flags again.
+ }
+}
+
+void MemBuf::freeInternalBuf(void* /* buf */, void* userData) {
+ auto* storage = static_cast<HeapStorage*>(userData);
+ releaseStorage(storage, kDataInUse);
+}
+
+MemBuf::MemBuf(CreateOp, std::size_t capacity)
+ : next_(this),
+ prev_(this),
+ data_(nullptr),
+ length_(0),
+ flags_and_shared_info_(0) {
+ SharedInfo* info;
+ allocExtBuffer(capacity, &buf_, &info, &capacity_);
+ setSharedInfo(info);
+ data_ = buf_;
+}
+
+MemBuf::MemBuf(CopyBufferOp /* op */, const void* buf, std::size_t size,
+ std::size_t headroom, std::size_t min_tailroom)
+ : MemBuf(CREATE, headroom + size + min_tailroom) {
+ advance(headroom);
+ if (size > 0) {
+ assert(buf != nullptr);
+ memcpy(writableData(), buf, size);
+ append(size);
+ }
+}
+
+unique_ptr<MemBuf> MemBuf::create(std::size_t capacity) {
+ // For smaller-sized buffers, allocate the MemBuf, SharedInfo, and the buffer
+ // all with a single allocation.
+ //
+ // We don't do this for larger buffers since it can be wasteful if the user
+ // needs to reallocate the buffer but keeps using the same MemBuf object.
+ // In this case we can't free the data space until the MemBuf is also
+ // destroyed. Callers can explicitly call createCombined() or
+ // createSeparate() if they know their use case better, and know if they are
+ // likely to reallocate the buffer later.
+ if (capacity <= kDefaultCombinedBufSize) {
+ return createCombined(capacity);
+ }
+ return createSeparate(capacity);
+}
+
+unique_ptr<MemBuf> MemBuf::createCombined(std::size_t capacity) {
+ // To save a memory allocation, allocate space for the MemBuf object, the
+ // SharedInfo struct, and the data itself all with a single call to malloc().
+ size_t requiredStorage = offsetof(HeapFullStorage, align) + capacity;
+ size_t mallocSize = requiredStorage;
+ auto* storage = static_cast<HeapFullStorage*>(malloc(mallocSize));
+
+ new (&storage->hs.prefix) HeapPrefix(kMemBufInUse | kDataInUse);
+ new (&storage->shared) SharedInfo(freeInternalBuf, storage);
+
+ uint8_t* bufAddr = reinterpret_cast<uint8_t*>(&storage->align);
+ uint8_t* storageEnd = reinterpret_cast<uint8_t*>(storage) + mallocSize;
+ size_t actualCapacity = size_t(storageEnd - bufAddr);
+ unique_ptr<MemBuf> ret(new (&storage->hs.buf) MemBuf(
+ InternalConstructor(), packFlagsAndSharedInfo(0, &storage->shared),
+ bufAddr, actualCapacity, bufAddr, 0));
+ return ret;
+}
+
+unique_ptr<MemBuf> MemBuf::createSeparate(std::size_t capacity) {
+ return std::make_unique<MemBuf>(CREATE, capacity);
+}
+
+unique_ptr<MemBuf> MemBuf::createChain(size_t totalCapacity,
+ std::size_t maxBufCapacity) {
+ unique_ptr<MemBuf> out =
+ create(std::min(totalCapacity, size_t(maxBufCapacity)));
+ size_t allocatedCapacity = out->capacity();
+
+ while (allocatedCapacity < totalCapacity) {
+ unique_ptr<MemBuf> newBuf = create(
+ std::min(totalCapacity - allocatedCapacity, size_t(maxBufCapacity)));
+ allocatedCapacity += newBuf->capacity();
+ out->prependChain(std::move(newBuf));
+ }
+
+ return out;
+}
+
+MemBuf::MemBuf(TakeOwnershipOp, void* buf, std::size_t capacity,
+ std::size_t length, FreeFunction freeFn, void* userData,
+ bool freeOnError)
+ : next_(this),
+ prev_(this),
+ data_(static_cast<uint8_t*>(buf)),
+ buf_(static_cast<uint8_t*>(buf)),
+ length_(length),
+ capacity_(capacity),
+ flags_and_shared_info_(
+ packFlagsAndSharedInfo(flag_free_shared_info, nullptr)) {
+ try {
+ setSharedInfo(new SharedInfo(freeFn, userData));
+ } catch (...) {
+ takeOwnershipError(freeOnError, buf, freeFn, userData);
+ throw;
+ }
+}
+
+unique_ptr<MemBuf> MemBuf::takeOwnership(void* buf, std::size_t capacity,
+ std::size_t length,
+ FreeFunction freeFn, void* userData,
+ bool freeOnError) {
+ try {
+ // TODO: We could allocate the MemBuf object and SharedInfo all in a single
+ // memory allocation. We could use the existing HeapStorage class, and
+ // define a new kSharedInfoInUse flag. We could change our code to call
+ // releaseStorage(flag_free_shared_info) when this flag_free_shared_info,
+ // rather than directly calling delete.
+ //
+ // Note that we always pass freeOnError as false to the constructor.
+ // If the constructor throws we'll handle it below. (We have to handle
+ // allocation failures from std::make_unique too.)
+ return std::make_unique<MemBuf>(TAKE_OWNERSHIP, buf, capacity, length,
+ freeFn, userData, false);
+ } catch (...) {
+ takeOwnershipError(freeOnError, buf, freeFn, userData);
+ throw;
+ }
+}
+
+MemBuf::MemBuf(WrapBufferOp, const void* buf, std::size_t capacity) noexcept
+ : MemBuf(InternalConstructor(), 0,
+ // We cast away the const-ness of the buffer here.
+ // This is okay since MemBuf users must use unshare() to create a
+ // copy of this buffer before writing to the buffer.
+ static_cast<uint8_t*>(const_cast<void*>(buf)), capacity,
+ static_cast<uint8_t*>(const_cast<void*>(buf)), capacity) {}
+
+unique_ptr<MemBuf> MemBuf::wrapBuffer(const void* buf, std::size_t capacity) {
+ return std::make_unique<MemBuf>(WRAP_BUFFER, buf, capacity);
+}
+
+MemBuf MemBuf::wrapBufferAsValue(const void* buf,
+ std::size_t capacity) noexcept {
+ return MemBuf(WrapBufferOp::WRAP_BUFFER, buf, capacity);
+}
+
+MemBuf::MemBuf() noexcept {}
+
+MemBuf::MemBuf(MemBuf&& other) noexcept
+ : data_(other.data_),
+ buf_(other.buf_),
+ length_(other.length_),
+ capacity_(other.capacity_),
+ flags_and_shared_info_(other.flags_and_shared_info_) {
+ // Reset other so it is a clean state to be destroyed.
+ other.data_ = nullptr;
+ other.buf_ = nullptr;
+ other.length_ = 0;
+ other.capacity_ = 0;
+ other.flags_and_shared_info_ = 0;
+
+ // If other was part of the chain, assume ownership of the rest of its chain.
+ // (It's only valid to perform move assignment on the head of a chain.)
+ if (other.next_ != &other) {
+ next_ = other.next_;
+ next_->prev_ = this;
+ other.next_ = &other;
+
+ prev_ = other.prev_;
+ prev_->next_ = this;
+ other.prev_ = &other;
+ }
+}
+
+MemBuf::MemBuf(const MemBuf& other) { *this = other.cloneAsValue(); }
+
+MemBuf::MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf,
+ std::size_t capacity, uint8_t* data, std::size_t length) noexcept
+ : next_(this),
+ prev_(this),
+ data_(data),
+ buf_(buf),
+ length_(length),
+ capacity_(capacity),
+ flags_and_shared_info_(flagsAndSharedInfo) {
+ assert(data >= buf);
+ assert(data + length <= buf + capacity);
+}
+
+MemBuf::~MemBuf() {
+ // Destroying an MemBuf destroys the entire chain.
+ // Users of MemBuf should only explicitly delete the head of any chain.
+ // The other elements in the chain will be automatically destroyed.
+ while (next_ != this) {
+ // Since unlink() returns unique_ptr() and we don't store it,
+ // it will automatically delete the unlinked element.
+ (void)next_->unlink();
+ }
+
+ decrementRefcount();
+}
+
+MemBuf& MemBuf::operator=(MemBuf&& other) noexcept {
+ if (this == &other) {
+ return *this;
+ }
+
+ // If we are part of a chain, delete the rest of the chain.
+ while (next_ != this) {
+ // Since unlink() returns unique_ptr() and we don't store it,
+ // it will automatically delete the unlinked element.
+ (void)next_->unlink();
+ }
+
+ // Decrement our refcount on the current buffer
+ decrementRefcount();
+
+ // Take ownership of the other buffer's data
+ data_ = other.data_;
+ buf_ = other.buf_;
+ length_ = other.length_;
+ capacity_ = other.capacity_;
+ flags_and_shared_info_ = other.flags_and_shared_info_;
+ // Reset other so it is a clean state to be destroyed.
+ other.data_ = nullptr;
+ other.buf_ = nullptr;
+ other.length_ = 0;
+ other.capacity_ = 0;
+ other.flags_and_shared_info_ = 0;
+
+ // If other was part of the chain, assume ownership of the rest of its chain.
+ // (It's only valid to perform move assignment on the head of a chain.)
+ if (other.next_ != &other) {
+ next_ = other.next_;
+ next_->prev_ = this;
+ other.next_ = &other;
+
+ prev_ = other.prev_;
+ prev_->next_ = this;
+ other.prev_ = &other;
+ }
+
+ return *this;
+}
+
+MemBuf& MemBuf::operator=(const MemBuf& other) {
+ if (this != &other) {
+ *this = MemBuf(other);
+ }
+ return *this;
+}
+
+bool MemBuf::empty() const {
+ const MemBuf* current = this;
+ do {
+ if (current->length() != 0) {
+ return false;
+ }
+ current = current->next_;
+ } while (current != this);
+ return true;
+}
+
+size_t MemBuf::countChainElements() const {
+ size_t numElements = 1;
+ for (MemBuf* current = next_; current != this; current = current->next_) {
+ ++numElements;
+ }
+ return numElements;
+}
+
+std::size_t MemBuf::computeChainDataLength() const {
+ std::size_t fullLength = length_;
+ for (MemBuf* current = next_; current != this; current = current->next_) {
+ fullLength += current->length_;
+ }
+ return fullLength;
+}
+
+void MemBuf::prependChain(unique_ptr<MemBuf>&& iobuf) {
+ // Take ownership of the specified MemBuf
+ MemBuf* other = iobuf.release();
+
+ // Remember the pointer to the tail of the other chain
+ MemBuf* otherTail = other->prev_;
+
+ // Hook up prev_->next_ to point at the start of the other chain,
+ // and other->prev_ to point at prev_
+ prev_->next_ = other;
+ other->prev_ = prev_;
+
+ // Hook up otherTail->next_ to point at us,
+ // and prev_ to point back at otherTail,
+ otherTail->next_ = this;
+ prev_ = otherTail;
+}
+
+unique_ptr<MemBuf> MemBuf::clone() const {
+ return std::make_unique<MemBuf>(cloneAsValue());
+}
+
+unique_ptr<MemBuf> MemBuf::cloneOne() const {
+ return std::make_unique<MemBuf>(cloneOneAsValue());
+}
+
+unique_ptr<MemBuf> MemBuf::cloneCoalesced() const {
+ return std::make_unique<MemBuf>(cloneCoalescedAsValue());
+}
+
+unique_ptr<MemBuf> MemBuf::cloneCoalescedWithHeadroomTailroom(
+ std::size_t new_headroom, std::size_t new_tailroom) const {
+ return std::make_unique<MemBuf>(
+ cloneCoalescedAsValueWithHeadroomTailroom(new_headroom, new_tailroom));
+}
+
+MemBuf MemBuf::cloneAsValue() const {
+ auto tmp = cloneOneAsValue();
+
+ for (MemBuf* current = next_; current != this; current = current->next_) {
+ tmp.prependChain(current->cloneOne());
+ }
+
+ return tmp;
+}
+
+MemBuf MemBuf::cloneOneAsValue() const {
+ if (SharedInfo* info = sharedInfo()) {
+ setFlags(flag_maybe_shared);
+ info->refcount.fetch_add(1, std::memory_order_acq_rel);
+ }
+ return MemBuf(InternalConstructor(), flags_and_shared_info_, buf_, capacity_,
+ data_, length_);
+}
+
+MemBuf MemBuf::cloneCoalescedAsValue() const {
+ const std::size_t new_headroom = headroom();
+ const std::size_t new_tailroom = prev()->tailroom();
+ return cloneCoalescedAsValueWithHeadroomTailroom(new_headroom, new_tailroom);
+}
+
+MemBuf MemBuf::cloneCoalescedAsValueWithHeadroomTailroom(
+ std::size_t new_headroom, std::size_t new_tailroom) const {
+ if (!isChained()) {
+ return cloneOneAsValue();
+ }
+ // Coalesce into newBuf
+ const std::size_t new_length = computeChainDataLength();
+ const std::size_t new_capacity = new_length + new_headroom + new_tailroom;
+ MemBuf newBuf{CREATE, new_capacity};
+ newBuf.advance(new_headroom);
+
+ auto current = this;
+ do {
+ if (current->length() > 0) {
+ memcpy(newBuf.writableTail(), current->data(), current->length());
+ newBuf.append(current->length());
+ }
+ current = current->next();
+ } while (current != this);
+
+ return newBuf;
+}
+
+void MemBuf::unshareOneSlow() {
+ // Allocate a new buffer for the data
+ uint8_t* buf;
+ SharedInfo* sharedInfo;
+ std::size_t actualCapacity;
+ allocExtBuffer(capacity_, &buf, &sharedInfo, &actualCapacity);
+
+ // Copy the data
+ // Maintain the same amount of headroom. Since we maintained the same
+ // minimum capacity we also maintain at least the same amount of tailroom.
+ std::size_t headlen = headroom();
+ if (length_ > 0) {
+ assert(data_ != nullptr);
+ memcpy(buf + headlen, data_, length_);
+ }
+
+ // Release our reference on the old buffer
+ decrementRefcount();
+ // Make sure flag_maybe_shared and flag_free_shared_info are all cleared.
+ setFlagsAndSharedInfo(0, sharedInfo);
+
+ // Update the buffer pointers to point to the new buffer
+ data_ = buf + headlen;
+ buf_ = buf;
+}
+
+void MemBuf::unshareChained() {
+ // unshareChained() should only be called if we are part of a chain of
+ // multiple MemBufs. The caller should have already verified this.
+ assert(isChained());
+
+ MemBuf* current = this;
+ while (true) {
+ if (current->isSharedOne()) {
+ // we have to unshare
+ break;
+ }
+
+ current = current->next_;
+ if (current == this) {
+ // None of the MemBufs in the chain are shared,
+ // so return without doing anything
+ return;
+ }
+ }
+
+ // We have to unshare. Let coalesceSlow() do the work.
+ coalesceSlow();
+}
+
+void MemBuf::markExternallyShared() {
+ MemBuf* current = this;
+ do {
+ current->markExternallySharedOne();
+ current = current->next_;
+ } while (current != this);
+}
+
+void MemBuf::makeManagedChained() {
+ assert(isChained());
+
+ MemBuf* current = this;
+ while (true) {
+ current->makeManagedOne();
+ current = current->next_;
+ if (current == this) {
+ break;
+ }
+ }
+}
+
+void MemBuf::coalesceSlow() {
+ // coalesceSlow() should only be called if we are part of a chain of multiple
+ // MemBufs. The caller should have already verified this.
+
+ // Compute the length of the entire chain
+ std::size_t new_length = 0;
+ MemBuf* end = this;
+ do {
+ new_length += end->length_;
+ end = end->next_;
+ } while (end != this);
+
+ coalesceAndReallocate(new_length, end);
+ // We should be only element left in the chain now
+}
+
+void MemBuf::coalesceSlow(size_t max_length) {
+ // coalesceSlow() should only be called if we are part of a chain of multiple
+ // MemBufs. The caller should have already verified this.
+
+ // Compute the length of the entire chain
+ std::size_t new_length = 0;
+ MemBuf* end = this;
+ while (true) {
+ new_length += end->length_;
+ end = end->next_;
+ if (new_length >= max_length) {
+ break;
+ }
+ if (end == this) {
+ throw std::overflow_error(
+ "attempted to coalesce more data than "
+ "available");
+ }
+ }
+
+ coalesceAndReallocate(new_length, end);
+ // We should have the requested length now
+}
+
+void MemBuf::coalesceAndReallocate(size_t new_headroom, size_t new_length,
+ MemBuf* end, size_t new_tailroom) {
+ std::size_t new_capacity = new_length + new_headroom + new_tailroom;
+
+ // Allocate space for the coalesced buffer.
+ // We always convert to an external buffer, even if we happened to be an
+ // internal buffer before.
+ uint8_t* newBuf;
+ SharedInfo* newInfo;
+ std::size_t actualCapacity;
+ allocExtBuffer(new_capacity, &newBuf, &newInfo, &actualCapacity);
+
+ // Copy the data into the new buffer
+ uint8_t* new_data = newBuf + new_headroom;
+ uint8_t* p = new_data;
+ MemBuf* current = this;
+ size_t remaining = new_length;
+ do {
+ if (current->length_ > 0) {
+ assert(current->length_ <= remaining);
+ assert(current->data_ != nullptr);
+ remaining -= current->length_;
+ memcpy(p, current->data_, current->length_);
+ p += current->length_;
+ }
+ current = current->next_;
+ } while (current != end);
+ assert(remaining == 0);
+
+ // Point at the new buffer
+ decrementRefcount();
+
+ // Make sure flag_maybe_shared and flag_free_shared_info are all cleared.
+ setFlagsAndSharedInfo(0, newInfo);
+
+ capacity_ = actualCapacity;
+ buf_ = newBuf;
+ data_ = new_data;
+ length_ = new_length;
+
+ // Separate from the rest of our chain.
+ // Since we don't store the unique_ptr returned by separateChain(),
+ // this will immediately delete the returned subchain.
+ if (isChained()) {
+ (void)separateChain(next_, current->prev_);
+ }
+}
+
+void MemBuf::decrementRefcount() {
+ // Externally owned buffers don't have a SharedInfo object and aren't managed
+ // by the reference count
+ SharedInfo* info = sharedInfo();
+ if (!info) {
+ return;
+ }
+
+ // Decrement the refcount
+ uint32_t newcnt = info->refcount.fetch_sub(1, std::memory_order_acq_rel);
+ // Note that fetch_sub() returns the value before we decremented.
+ // If it is 1, we were the only remaining user; if it is greater there are
+ // still other users.
+ if (newcnt > 1) {
+ return;
+ }
+
+ // We were the last user. Free the buffer
+ freeExtBuffer();
+
+ // Free the SharedInfo if it was allocated separately.
+ //
+ // This is only used by takeOwnership().
+ //
+ // To avoid this special case handling in decrementRefcount(), we could have
+ // takeOwnership() set a custom freeFn() that calls the user's free function
+ // then frees the SharedInfo object. (This would require that
+ // takeOwnership() store the user's free function with its allocated
+ // SharedInfo object.) However, handling this specially with a flag seems
+ // like it shouldn't be problematic.
+ if (flags() & flag_free_shared_info) {
+ delete sharedInfo();
+ }
+}
+
+void MemBuf::reserveSlow(std::size_t min_headroom, std::size_t min_tailroom) {
+ size_t new_capacity = (size_t)length_ + min_headroom + min_tailroom;
+
+ // // reserveSlow() is dangerous if anyone else is sharing the buffer, as we
+ // may
+ // // reallocate and free the original buffer. It should only ever be called
+ // if
+ // // we are the only user of the buffer.
+
+ // We'll need to reallocate the buffer.
+ // There are a few options.
+ // - If we have enough total room, move the data around in the buffer
+ // and adjust the data_ pointer.
+ // - If we're using an internal buffer, we'll switch to an external
+ // buffer with enough headroom and tailroom.
+ // - If we have enough headroom (headroom() >= min_headroom) but not too much
+ // (so we don't waste memory), we can try:
+ // - If we don't have too much to copy, we'll use realloc() (note that
+ // realloc might have to copy
+ // headroom + data + tailroom)
+ // - Otherwise, bite the bullet and reallocate.
+ if (headroom() + tailroom() >= min_headroom + min_tailroom) {
+ uint8_t* new_data = writableBuffer() + min_headroom;
+ std::memmove(new_data, data_, length_);
+ data_ = new_data;
+ return;
+ }
+
+ size_t new_allocated_capacity = 0;
+ uint8_t* new_buffer = nullptr;
+ std::size_t new_headroom = 0;
+ std::size_t old_headroom = headroom();
+
+ // If we have a buffer allocated with malloc and we just need more tailroom,
+ // try to use realloc()/xallocx() to grow the buffer in place.
+ SharedInfo* info = sharedInfo();
+ if (info && (info->freeFn == nullptr) && length_ != 0 &&
+ old_headroom >= min_headroom) {
+ size_t head_slack = old_headroom - min_headroom;
+ new_allocated_capacity = goodExtBufferSize(new_capacity + head_slack);
+
+ size_t copySlack = capacity() - length_;
+ if (copySlack * 2 <= length_) {
+ void* p = realloc(buf_, new_allocated_capacity);
+ if (TRANSPORT_EXPECT_FALSE(p == nullptr)) {
+ throw std::bad_alloc();
+ }
+ new_buffer = static_cast<uint8_t*>(p);
+ new_headroom = old_headroom;
+ }
+ }
+
+ // None of the previous reallocation strategies worked (or we're using
+ // an internal buffer). malloc/copy/free.
+ if (new_buffer == nullptr) {
+ new_allocated_capacity = goodExtBufferSize(new_capacity);
+ new_buffer = static_cast<uint8_t*>(malloc(new_allocated_capacity));
+ if (length_ > 0) {
+ assert(data_ != nullptr);
+ memcpy(new_buffer + min_headroom, data_, length_);
+ }
+ if (sharedInfo()) {
+ freeExtBuffer();
+ }
+ new_headroom = min_headroom;
+ }
+
+ std::size_t cap;
+ initExtBuffer(new_buffer, new_allocated_capacity, &info, &cap);
+
+ if (flags() & flag_free_shared_info) {
+ delete sharedInfo();
+ }
+
+ setFlagsAndSharedInfo(0, info);
+ capacity_ = cap;
+ buf_ = new_buffer;
+ data_ = new_buffer + new_headroom;
+ // length_ is unchanged
+}
+
+void MemBuf::freeExtBuffer() {
+ SharedInfo* info = sharedInfo();
+
+ if (info->freeFn) {
+ try {
+ info->freeFn(buf_, info->userData);
+ } catch (...) {
+ // The user's free function should never throw. Otherwise we might
+ // throw from the MemBuf destructor. Other code paths like coalesce()
+ // also assume that decrementRefcount() cannot throw.
+ abort();
+ }
+ } else {
+ free(buf_);
+ }
+}
+
+void MemBuf::allocExtBuffer(std::size_t minCapacity, uint8_t** bufReturn,
+ SharedInfo** infoReturn,
+ std::size_t* capacityReturn) {
+ size_t mallocSize = goodExtBufferSize(minCapacity);
+ uint8_t* buf = static_cast<uint8_t*>(malloc(mallocSize));
+ initExtBuffer(buf, mallocSize, infoReturn, capacityReturn);
+ *bufReturn = buf;
+}
+
+size_t MemBuf::goodExtBufferSize(std::size_t minCapacity) {
+ // Determine how much space we should allocate. We'll store the SharedInfo
+ // for the external buffer just after the buffer itself. (We store it just
+ // after the buffer rather than just before so that the code can still just
+ // use free(buf_) to free the buffer.)
+ size_t minSize = static_cast<size_t>(minCapacity) + sizeof(SharedInfo);
+ // Add room for padding so that the SharedInfo will be aligned on an 8-byte
+ // boundary.
+ minSize = (minSize + 7) & ~7;
+
+ // Use goodMallocSize() to bump up the capacity to a decent size to request
+ // from malloc, so we can use all of the space that malloc will probably give
+ // us anyway.
+ return minSize;
+}
+
+void MemBuf::initExtBuffer(uint8_t* buf, size_t mallocSize,
+ SharedInfo** infoReturn,
+ std::size_t* capacityReturn) {
+ // Find the SharedInfo storage at the end of the buffer
+ // and construct the SharedInfo.
+ uint8_t* infoStart = (buf + mallocSize) - sizeof(SharedInfo);
+ SharedInfo* sharedInfo = new (infoStart) SharedInfo;
+
+ *capacityReturn = std::size_t(infoStart - buf);
+ *infoReturn = sharedInfo;
+}
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/utils/memory_pool_allocator.h b/libtransport/src/utils/memory_pool_allocator.h
new file mode 100644
index 000000000..adc1443ad
--- /dev/null
+++ b/libtransport/src/utils/memory_pool_allocator.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.sudo make instamake install
+ */
+
+#pragma once
+
+#include <memory>
+
+namespace utils {
+
+template <class T, std::size_t growSize = 1024>
+class MemoryPool {
+ struct Block {
+ Block *next;
+ };
+
+ class Buffer {
+ static const std::size_t blockSize = sizeof(T) > sizeof(Block)
+ ? sizeof(T)
+ : sizeof(Block);
+ uint8_t data[blockSize * growSize];
+
+ public:
+ Buffer *const next;
+
+ Buffer(Buffer *next) : next(next) {}
+
+ T *getBlock(std::size_t index) {
+ return reinterpret_cast<T *>(&data[blockSize * index]);
+ }
+ };
+
+ Block *firstFreeBlock = nullptr;
+ Buffer *firstBuffer = nullptr;
+ std::size_t bufferedBlocks = growSize;
+
+ public:
+ MemoryPool() = default;
+ MemoryPool(MemoryPool &&memoryPool) = delete;
+ MemoryPool(const MemoryPool &memoryPool) = delete;
+ MemoryPool operator=(MemoryPool &&memoryPool) = delete;
+ MemoryPool operator=(const MemoryPool &memoryPool) = delete;
+
+ ~MemoryPool() {
+ while (firstBuffer) {
+ Buffer *buffer = firstBuffer;
+ firstBuffer = buffer->next;
+ delete buffer;
+ }
+ }
+
+ T *allocate() {
+ if (firstFreeBlock) {
+ Block *block = firstFreeBlock;
+ firstFreeBlock = block->next;
+ return reinterpret_cast<T *>(block);
+ }
+
+ if (bufferedBlocks >= growSize) {
+ firstBuffer = new Buffer(firstBuffer);
+ bufferedBlocks = 0;
+ }
+
+ return firstBuffer->getBlock(bufferedBlocks++);
+ }
+
+ void deallocate(T *pointer) {
+ Block *block = reinterpret_cast<Block *>(pointer);
+ block->next = firstFreeBlock;
+ firstFreeBlock = block;
+ }
+};
+
+template <class T, std::size_t growSize = 1024>
+class Allocator : private MemoryPool<T, growSize> {
+#ifdef _WIN32
+ Allocator *copyAllocator;
+ std::allocator<T> *rebindAllocator = nullptr;
+#endif
+
+ public:
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef T *pointer;
+ typedef const T *const_pointer;
+ typedef T &reference;
+ typedef const T &const_reference;
+ typedef T value_type;
+
+ template <class U>
+ struct rebind {
+ typedef Allocator<U, growSize> other;
+ };
+
+#ifdef _WIN32
+ Allocator() = default;
+
+ Allocator(Allocator &allocator) : copyAllocator(&allocator) {}
+
+ template <class U>
+ Allocator(const Allocator<U, growSize> &other) {
+ if (!std::is_same<T, U>::value) rebindAllocator = new std::allocator<T>();
+ }
+
+ ~Allocator() { delete rebindAllocator; }
+#endif
+
+ pointer allocate(size_type n, const void *hint = 0) {
+#ifdef _WIN32
+ if (copyAllocator) return copyAllocator->allocate(n, hint);
+
+ if (rebindAllocator) return rebindAllocator->allocate(n, hint);
+#endif
+
+ if (n != 1 || hint) throw std::bad_alloc();
+
+ return MemoryPool<T, growSize>::allocate();
+ }
+
+ void deallocate(pointer p, size_type n) {
+#ifdef _WIN32
+ if (copyAllocator) {
+ copyAllocator->deallocate(p, n);
+ return;
+ }
+
+ if (rebindAllocator) {
+ rebindAllocator->deallocate(p, n);
+ return;
+ }
+#endif
+
+ MemoryPool<T, growSize>::deallocate(p);
+ }
+
+ void construct(pointer p, const_reference val) { new (p) T(val); }
+
+ void destroy(pointer p) { p->~T(); }
+};
+
+} \ No newline at end of file
diff --git a/libtransport/src/utils/min_filter.h b/libtransport/src/utils/min_filter.h
new file mode 100644
index 000000000..dcfd5652d
--- /dev/null
+++ b/libtransport/src/utils/min_filter.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.sudo make instamake install
+ */
+
+#pragma once
+
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/log.h>
+
+#include <deque>
+#include <iostream>
+#include <set>
+#include <type_traits>
+#include <vector>
+
+namespace utils {
+
+template <typename T>
+class MinFilter {
+ public:
+ MinFilter(std::size_t size) : size_(size) {}
+
+ std::size_t size() { return by_arrival_.size(); }
+
+ template <typename R>
+ TRANSPORT_ALWAYS_INLINE void pushBack(R&& value) {
+ if (by_arrival_.size() >= size_) {
+ by_order_.erase(by_arrival_.back());
+ by_arrival_.pop_back();
+ }
+
+ by_arrival_.push_front(by_order_.insert(std::forward<R>(value)));
+ }
+
+ TRANSPORT_ALWAYS_INLINE const T& begin() { return *by_order_.cbegin(); }
+
+ TRANSPORT_ALWAYS_INLINE const T& rBegin() { return *by_order_.crbegin(); }
+
+ private:
+ std::multiset<T> by_order_;
+ std::deque<typename std::multiset<T>::const_iterator> by_arrival_;
+ std::size_t size_;
+};
+
+} // namespace utils
diff --git a/libtransport/src/utils/stream_buffer.h b/libtransport/src/utils/stream_buffer.h
new file mode 100644
index 000000000..adfb696f2
--- /dev/null
+++ b/libtransport/src/utils/stream_buffer.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <streambuf>
+
+namespace utils {
+
+template <typename char_type>
+struct ostreambuf
+ : public std::basic_streambuf<char_type, std::char_traits<char_type> > {
+ ostreambuf(char_type* buffer, std::streamsize buffer_length) {
+ // set the "put" pointer the start of the buffer and record it's length.
+ setp(buffer, buffer + buffer_length);
+ }
+};
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/utils/string_tokenizer.cc b/libtransport/src/utils/string_tokenizer.cc
new file mode 100644
index 000000000..a280a3c43
--- /dev/null
+++ b/libtransport/src/utils/string_tokenizer.cc
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/errors/errors.h>
+#include <hicn/transport/utils/string_tokenizer.h>
+
+namespace utils {
+
+StringTokenizer::StringTokenizer(const std::string &str)
+ : str_(str), delimiter_(" ") {}
+
+StringTokenizer::StringTokenizer(const std::string &str,
+ const std::string &delim)
+ : str_(str), delimiter_(delim) {}
+
+bool StringTokenizer::hasMoreTokens() {
+ return str_.find(delimiter_) != std::string::npos || !str_.empty();
+}
+
+std::string StringTokenizer::nextToken() {
+ unsigned long pos = (unsigned long)str_.find(delimiter_);
+
+ bool token_found = std::string::npos != pos;
+
+ if (!token_found && str_.empty()) {
+ throw errors::TokenizerException();
+ }
+
+ std::string token = str_.substr(0, pos);
+ str_.erase(0, token_found ? pos + delimiter_.length() : pos);
+
+ return token;
+}
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/utils/suffix_strategy.h b/libtransport/src/utils/suffix_strategy.h
new file mode 100644
index 000000000..6c4dd2785
--- /dev/null
+++ b/libtransport/src/utils/suffix_strategy.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <core/manifest_format.h>
+
+namespace utils {
+
+using transport::core::NextSegmentCalculationStrategy;
+
+class SuffixStrategy {
+ public:
+ static constexpr uint32_t INVALID_SUFFIX =
+ std::numeric_limits<uint32_t>::max();
+
+ SuffixStrategy(NextSegmentCalculationStrategy strategy)
+ : suffix_stragegy_(strategy),
+ total_count_(0),
+ final_suffix_(INVALID_SUFFIX) {}
+
+ virtual ~SuffixStrategy() = default;
+
+ virtual uint32_t getNextSuffix() = 0;
+
+ virtual uint32_t getFinalSuffix() { return final_suffix_; }
+
+ virtual void setFinalSuffix(std::uint32_t final_suffix) {
+ if (final_suffix != INVALID_SUFFIX) {
+ final_suffix_ = final_suffix;
+ }
+ }
+
+ virtual uint32_t getNextManifestSuffix() = 0;
+
+ virtual uint32_t getNextContentSuffix() = 0;
+
+ virtual void reset(uint32_t offset = 0) = 0;
+
+ virtual uint32_t getManifestCapacity() = 0;
+
+ virtual void setManifestCapacity(uint32_t capacity) = 0;
+
+ virtual uint32_t getTotalCount() { return total_count_; };
+
+ NextSegmentCalculationStrategy getSuffixStrategy() {
+ return suffix_stragegy_;
+ }
+
+ protected:
+ inline void incrementTotalCount() { total_count_++; };
+
+ protected:
+ NextSegmentCalculationStrategy suffix_stragegy_;
+ std::uint32_t total_count_;
+ std::uint32_t final_suffix_;
+};
+
+class IncrementalSuffixStrategy : public SuffixStrategy {
+ public:
+ IncrementalSuffixStrategy(std::uint32_t start_offset)
+ : SuffixStrategy(NextSegmentCalculationStrategy::INCREMENTAL),
+ next_suffix_(start_offset) {}
+
+ TRANSPORT_ALWAYS_INLINE std::uint32_t getNextSuffix() override {
+ incrementTotalCount();
+ return next_suffix_++;
+ }
+
+ TRANSPORT_ALWAYS_INLINE std::uint32_t getNextContentSuffix() override {
+ return getNextSuffix();
+ }
+
+ TRANSPORT_ALWAYS_INLINE std::uint32_t getNextManifestSuffix() override {
+ return getNextSuffix();
+ }
+
+ uint32_t getManifestCapacity() override {
+ throw errors::RuntimeException(
+ "No manifest capacity in IncrementalSuffixStrategy.");
+ }
+
+ void setManifestCapacity(uint32_t capacity) override {
+ throw errors::RuntimeException(
+ "No manifest capacity in IncrementalSuffixStrategy.");
+ }
+
+ void reset(std::uint32_t offset = 0) override { next_suffix_ = offset; }
+
+ protected:
+ std::uint32_t next_suffix_;
+};
+
+class CapacityBasedSuffixStrategy : public SuffixStrategy {
+ public:
+ CapacityBasedSuffixStrategy(std::uint32_t start_offset,
+ std::uint32_t manifest_capacity)
+ : SuffixStrategy(NextSegmentCalculationStrategy::INCREMENTAL),
+ next_suffix_(start_offset),
+ segments_in_manifest_(manifest_capacity),
+ current_manifest_iteration_(0) {}
+
+ TRANSPORT_ALWAYS_INLINE std::uint32_t getNextSuffix() override {
+ incrementTotalCount();
+ return next_suffix_++;
+ }
+
+ TRANSPORT_ALWAYS_INLINE std::uint32_t getNextContentSuffix() override {
+ incrementTotalCount();
+ return next_suffix_ % segments_in_manifest_ == 0 ? next_suffix_++
+ : ++next_suffix_;
+ }
+
+ TRANSPORT_ALWAYS_INLINE std::uint32_t getNextManifestSuffix() override {
+ incrementTotalCount();
+ return (current_manifest_iteration_++) * (segments_in_manifest_ + 1);
+ }
+
+ TRANSPORT_ALWAYS_INLINE uint32_t getManifestCapacity() override {
+ return segments_in_manifest_;
+ }
+
+ TRANSPORT_ALWAYS_INLINE void setManifestCapacity(uint32_t capacity) override {
+ segments_in_manifest_ = capacity;
+ }
+
+ void reset(std::uint32_t offset = 0) override { next_suffix_ = offset; }
+
+ protected:
+ std::uint32_t next_suffix_;
+ std::uint32_t segments_in_manifest_;
+ std::uint32_t current_manifest_iteration_;
+};
+
+class SuffixStrategyFactory {
+ public:
+ static std::unique_ptr<SuffixStrategy> getSuffixStrategy(
+ NextSegmentCalculationStrategy strategy, uint32_t start_offset,
+ uint32_t manifest_capacity = 0) {
+ switch (strategy) {
+ case NextSegmentCalculationStrategy::INCREMENTAL:
+ return std::make_unique<IncrementalSuffixStrategy>(start_offset);
+ case NextSegmentCalculationStrategy::MANIFEST_CAPACITY_BASED:
+ return std::make_unique<CapacityBasedSuffixStrategy>(start_offset,
+ manifest_capacity);
+ default:
+ throw errors::RuntimeException(
+ "No valid NextSegmentCalculationStrategy specified.");
+ }
+ }
+};
+
+} // namespace utils
diff --git a/libtransport/src/utils/test.h b/libtransport/src/utils/test.h
new file mode 100644
index 000000000..e3dd619ac
--- /dev/null
+++ b/libtransport/src/utils/test.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sstream>
+
+namespace testing {
+
+namespace internal {
+
+enum GTestColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW };
+
+extern void ColoredPrintf(GTestColor color, const char *fmt, ...);
+
+} // namespace internal
+
+} // namespace testing
+
+#define PRINTF(...) \
+ do { \
+ testing::internal::ColoredPrintf(testing::internal::COLOR_GREEN, \
+ "[ ] "); \
+ testing::internal::ColoredPrintf(testing::internal::COLOR_YELLOW, \
+ __VA_ARGS__); \
+ } while (0)
+
+// C++ stream interface
+class TestCout : public std::stringstream {
+ public:
+ ~TestCout() {}
+};
+
+#define TEST_COUT TestCout() \ No newline at end of file
diff --git a/libtransport/src/utils/uri.cc b/libtransport/src/utils/uri.cc
new file mode 100644
index 000000000..33eb8b45b
--- /dev/null
+++ b/libtransport/src/utils/uri.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/errors/runtime_exception.h>
+#include <hicn/transport/utils/uri.h>
+
+namespace utils {
+
+Uri::Uri() {}
+
+Uri &Uri::parse(const std::string &uri) {
+ if (uri.length() == 0) {
+ throw errors::RuntimeException("Malformed URI.");
+ }
+
+ iterator_t uri_end = uri.end();
+
+ // get query start
+ iterator_t query_start = std::find(uri.begin(), uri_end, '?');
+
+ // protocol
+ iterator_t protocol_start = uri.begin();
+ iterator_t protocol_end = std::find(protocol_start, uri_end, ':'); //"://");
+
+ if (protocol_end != uri_end) {
+ std::string prot = &*(protocol_end);
+ if ((prot.length() > 3) && (prot.substr(0, 3) == "://")) {
+ protocol_ = std::string(protocol_start, protocol_end);
+ protocol_end += 3; // ://
+ } else {
+ protocol_end = uri.begin(); // no protocol
+ }
+ } else {
+ protocol_end = uri.begin(); // no protocol
+ }
+ // host
+ iterator_t host_start = protocol_end;
+ iterator_t path_start =
+ std::find(host_start, uri_end, '/'); // get path_start
+
+ iterator_t host_end = std::find(
+ protocol_end, (path_start != uri_end) ? path_start : query_start,
+ ':'); // check for port
+
+ locator_ = std::string(host_start, host_end);
+
+ // port
+ if ((host_end != uri_end) && ((&*(host_end))[0] == ':')) {
+ host_end++;
+ iterator_t port_end = (path_start != uri_end) ? path_start : query_start;
+ port_ = std::string(host_end, port_end);
+ }
+
+ // path
+ if (path_start != uri_end) {
+ path_ = std::string(path_start, query_start);
+ }
+ // query
+ if (query_start != uri_end) {
+ query_string_ = std::string(query_start, uri.end());
+ }
+
+ return *this;
+}
+
+Uri &Uri::parseProtocolAndLocator(const std::string &locator) {
+ iterator_t total_end = locator.end();
+
+ // protocol
+ iterator_t protocol_start = locator.begin();
+ iterator_t protocol_end =
+ std::find(protocol_start, total_end, ':'); //"://");
+
+ if (protocol_end != total_end) {
+ std::string prot = &*(protocol_end);
+ if ((prot.length() > 3) && (prot.substr(0, 3) == "://")) {
+ protocol_ = std::string(protocol_start, protocol_end);
+ protocol_end += 3; // ://
+ } else {
+ throw errors::RuntimeException("Malformed locator. (Missing \"://\")");
+ }
+ } else {
+ throw errors::RuntimeException("Malformed locator. No protocol specified.");
+ }
+
+ // locator
+ iterator_t host_start = protocol_end;
+ iterator_t host_end = std::find(protocol_end, total_end, '/');
+
+ if (host_start == host_end) {
+ throw errors::RuntimeException(
+ "Malformed locator. Locator name is missing");
+ }
+
+ locator_ = std::string(host_start, host_end);
+
+ return *this;
+}
+
+std::string Uri::getLocator() { return locator_; }
+
+std::string Uri::getPath() { return path_; }
+
+std::string Uri::getPort() { return port_; }
+
+std::string Uri::getProtocol() { return protocol_; }
+
+std::string Uri::getQueryString() { return query_string_; }
+
+} // end namespace utils