summaryrefslogtreecommitdiffstats
path: root/libtransport/src/hicn/transport/utils
diff options
context:
space:
mode:
Diffstat (limited to 'libtransport/src/hicn/transport/utils')
-rwxr-xr-xlibtransport/src/hicn/transport/utils/CMakeLists.txt76
-rwxr-xr-xlibtransport/src/hicn/transport/utils/array.h62
-rwxr-xr-xlibtransport/src/hicn/transport/utils/branch_prediction.h22
-rwxr-xr-xlibtransport/src/hicn/transport/utils/content_store.cc109
-rwxr-xr-xlibtransport/src/hicn/transport/utils/content_store.h75
-rwxr-xr-xlibtransport/src/hicn/transport/utils/conversions.h37
-rwxr-xr-xlibtransport/src/hicn/transport/utils/crypto_hash.h115
-rwxr-xr-xlibtransport/src/hicn/transport/utils/crypto_hash_type.h31
-rwxr-xr-xlibtransport/src/hicn/transport/utils/crypto_hasher.h68
-rwxr-xr-xlibtransport/src/hicn/transport/utils/crypto_suite.h35
-rwxr-xr-xlibtransport/src/hicn/transport/utils/daemonizator.cc73
-rwxr-xr-xlibtransport/src/hicn/transport/utils/daemonizator.h25
-rwxr-xr-xlibtransport/src/hicn/transport/utils/deadline_timer.h114
-rwxr-xr-xlibtransport/src/hicn/transport/utils/endianess.h136
-rwxr-xr-xlibtransport/src/hicn/transport/utils/epoll_event_reactor.cc183
-rwxr-xr-xlibtransport/src/hicn/transport/utils/epoll_event_reactor.h65
-rwxr-xr-xlibtransport/src/hicn/transport/utils/event_reactor.h37
-rwxr-xr-xlibtransport/src/hicn/transport/utils/event_thread.h101
-rwxr-xr-xlibtransport/src/hicn/transport/utils/fd_deadline_timer.h127
-rwxr-xr-xlibtransport/src/hicn/transport/utils/hash.h101
-rwxr-xr-xlibtransport/src/hicn/transport/utils/identity.cc130
-rwxr-xr-xlibtransport/src/hicn/transport/utils/identity.h64
-rwxr-xr-xlibtransport/src/hicn/transport/utils/key_id.h25
-rwxr-xr-xlibtransport/src/hicn/transport/utils/linux.h64
-rwxr-xr-xlibtransport/src/hicn/transport/utils/literals.h55
-rwxr-xr-xlibtransport/src/hicn/transport/utils/log.cc1405
-rwxr-xr-xlibtransport/src/hicn/transport/utils/log.h1057
-rwxr-xr-xlibtransport/src/hicn/transport/utils/membuf.cc864
-rwxr-xr-xlibtransport/src/hicn/transport/utils/membuf.h916
-rwxr-xr-xlibtransport/src/hicn/transport/utils/min_filter.h56
-rwxr-xr-xlibtransport/src/hicn/transport/utils/object_pool.h76
-rwxr-xr-xlibtransport/src/hicn/transport/utils/ring_buffer.h129
-rwxr-xr-xlibtransport/src/hicn/transport/utils/sharable_vector.h30
-rwxr-xr-xlibtransport/src/hicn/transport/utils/signer.cc173
-rwxr-xr-xlibtransport/src/hicn/transport/utils/signer.h69
-rwxr-xr-xlibtransport/src/hicn/transport/utils/socket.h267
-rwxr-xr-xlibtransport/src/hicn/transport/utils/spinlock.h53
-rwxr-xr-xlibtransport/src/hicn/transport/utils/stream_buffer.h31
-rwxr-xr-xlibtransport/src/hicn/transport/utils/string_tokenizer.cc47
-rwxr-xr-xlibtransport/src/hicn/transport/utils/string_tokenizer.h35
-rwxr-xr-xlibtransport/src/hicn/transport/utils/test.h46
-rwxr-xr-xlibtransport/src/hicn/transport/utils/uri.cc122
-rwxr-xr-xlibtransport/src/hicn/transport/utils/uri.h47
-rwxr-xr-xlibtransport/src/hicn/transport/utils/verifier.cc193
-rwxr-xr-xlibtransport/src/hicn/transport/utils/verifier.h85
45 files changed, 7631 insertions, 0 deletions
diff --git a/libtransport/src/hicn/transport/utils/CMakeLists.txt b/libtransport/src/hicn/transport/utils/CMakeLists.txt
new file mode 100755
index 000000000..088fb5862
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/CMakeLists.txt
@@ -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.
+
+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}/daemonizator.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/min_filter.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/signer.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/verifier.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/identity.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}/array.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/string_tokenizer.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hash.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/uri.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemonizator.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/sharable_vector.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/branch_prediction.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/event_reactor.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/deadline_timer.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/ring_buffer.h
+ ${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}/endianess.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/literals.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/signer.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/verifier.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hasher.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/crypto_suite.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/crypto_hash_type.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/identity.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/conversions.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/linux.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/log.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/event_thread.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/object_pool.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/membuf.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/spinlock.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/content_store.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/key_id.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()
+
+set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
+set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
diff --git a/libtransport/src/hicn/transport/utils/array.h b/libtransport/src/hicn/transport/utils/array.h
new file mode 100755
index 000000000..a3a66e498
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/array.h
@@ -0,0 +1,62 @@
+/*
+ * 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/portability/portability.h>
+
+#include <cstddef>
+
+namespace utils {
+
+template <typename T>
+class Array {
+ public:
+ explicit Array(const T *array, size_t size) : array_(array), size_(size) {
+ this->array_ = array;
+ this->size_ = size;
+ }
+
+ Array() : array_(nullptr), size_(0) {
+ this->array_ = nullptr;
+ this->size_ = 0;
+ }
+
+ TRANSPORT_ALWAYS_INLINE const T *data() const { return array_; }
+
+ TRANSPORT_ALWAYS_INLINE T *writableData() const {
+ return const_cast<T *>(array_);
+ }
+
+ TRANSPORT_ALWAYS_INLINE std::size_t length() const { return size_; }
+
+ TRANSPORT_ALWAYS_INLINE Array &setData(const T *data) {
+ array_ = data;
+ return *this;
+ }
+
+ TRANSPORT_ALWAYS_INLINE Array &setSize(std::size_t size) {
+ size_ = size;
+ return *this;
+ }
+
+ TRANSPORT_ALWAYS_INLINE bool empty() { return !size_; }
+
+ private:
+ const T *array_;
+ std::size_t size_;
+};
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/branch_prediction.h b/libtransport/src/hicn/transport/utils/branch_prediction.h
new file mode 100755
index 000000000..b12282fe8
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/branch_prediction.h
@@ -0,0 +1,22 @@
+/*
+ * 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
+
+#undef TRANSPORT_EXPECT_TRUE
+#undef TRANSPORT_EXPECT_FALSE
+
+#define TRANSPORT_EXPECT_TRUE(x) __builtin_expect((x), 1)
+#define TRANSPORT_EXPECT_FALSE(x) __builtin_expect((x), 0) \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/content_store.cc b/libtransport/src/hicn/transport/utils/content_store.cc
new file mode 100755
index 000000000..4c7637dad
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/content_store.cc
@@ -0,0 +1,109 @@
+/*
+ * 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/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;
+ }
+
+ std::unique_lock<std::mutex> lock(cs_mutex_);
+
+ if (TRANSPORT_EXPECT_FALSE(content_store_hash_table_.size() !=
+ lru_list_.size())) {
+ TRANSPORT_LOGW("Inconsistent size!!!!");
+ TRANSPORT_LOGW("Hash Table: %zu |||| FIFO List: %zu",
+ content_store_hash_table_.size(), lru_list_.size());
+ }
+
+ // Check if the content can be cached
+ if (content_object->getLifetime() > 0) {
+ if (content_store_hash_table_.size() >= max_content_store_size_) {
+ content_store_hash_table_.erase(lru_list_.back());
+ lru_list_.pop_back();
+ }
+
+ // Insert new item
+
+ auto it = content_store_hash_table_.find(content_object->getName());
+ if (it != content_store_hash_table_.end()) {
+ lru_list_.erase(it->second.second);
+ content_store_hash_table_.erase(content_object->getName());
+ }
+
+ lru_list_.push_front(std::cref(content_object->getName()));
+ auto pos = lru_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) {
+ std::unique_lock<std::mutex> lock(cs_mutex_);
+ auto it = content_store_hash_table_.find(interest.getName());
+ if (it != content_store_hash_table_.end()) {
+ // if (std::chrono::duration_cast<std::chrono::milliseconds>(
+ // std::chrono::steady_clock::now() - it->second.first.second).count()
+ // < it->second.first.first->getLifetime() ||
+ // it->second.first.first->getLifetime() ==
+ // default_values::never_expire_time) {
+ return it->second.first.first;
+ // }
+ }
+
+ return empty_reference_;
+}
+
+void ContentStore::erase(const Name &exact_name) {
+ std::unique_lock<std::mutex> lock(cs_mutex_);
+ auto it = content_store_hash_table_.find(exact_name);
+ lru_list_.erase(it->second.second);
+ content_store_hash_table_.erase(exact_name);
+}
+
+void ContentStore::setLimit(size_t max_packets) {
+ max_content_store_size_ = max_packets;
+}
+
+std::size_t ContentStore::getLimit() const { return max_content_store_size_; }
+
+std::size_t ContentStore::size() const {
+ 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\n",
+ item.second.first.first->getName().toString().c_str());
+ }
+ }
+}
+
+} // end namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/content_store.h b/libtransport/src/hicn/transport/utils/content_store.h
new file mode 100755
index 000000000..ab4963fff
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/content_store.h
@@ -0,0 +1,75 @@
+/*
+ * 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/interfaces/socket.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>> LRUList;
+typedef std::unordered_map<Name, ContentStoreEntry> ContentStoreHashTable;
+
+class ContentStore {
+ public:
+ explicit ContentStore(std::size_t max_packets = 65536);
+
+ ~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_;
+ LRUList lru_list_;
+ std::shared_ptr<ContentObject> empty_reference_;
+ std::size_t max_content_store_size_;
+ std::mutex cs_mutex_;
+};
+
+} // end namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/conversions.h b/libtransport/src/hicn/transport/utils/conversions.h
new file mode 100755
index 000000000..24b529206
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/conversions.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 <hicn/transport/portability/portability.h>
+
+#include <stdio.h>
+#include <cstdint>
+#include <string>
+
+namespace utils {
+
+static TRANSPORT_ALWAYS_INLINE int convertStringToMacAddress(
+ const std::string& mac_address, uint8_t* mac_byte_array) {
+ const char* mac = mac_address.c_str();
+
+ sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mac_byte_array[0],
+ &mac_byte_array[1], &mac_byte_array[2], &mac_byte_array[3],
+ &mac_byte_array[4], &mac_byte_array[5]);
+
+ return 0;
+}
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/crypto_hash.h b/libtransport/src/hicn/transport/utils/crypto_hash.h
new file mode 100755
index 000000000..0c15c8bda
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/crypto_hash.h
@@ -0,0 +1,115 @@
+/*
+ * 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/portability/portability.h>
+#include <hicn/transport/utils/array.h>
+#include <hicn/transport/utils/crypto_hash_type.h>
+
+extern "C" {
+#include <parc/security/parc_CryptoHash.h>
+};
+
+#include <cstring>
+#include <unordered_map>
+
+namespace utils {
+
+class CryptoHasher;
+
+struct EnumClassHash {
+ template <typename T>
+ std::size_t operator()(T t) const {
+ return static_cast<std::size_t>(t);
+ }
+};
+
+static std::unordered_map<CryptoHashType, std::size_t, EnumClassHash>
+ hash_size_map = {{CryptoHashType::SHA_256, 32},
+ {CryptoHashType::CRC32C, 4},
+ {CryptoHashType::SHA_512, 64}};
+
+class Signer;
+class Verifier;
+
+class CryptoHash {
+ friend class CryptoHasher;
+ friend class Signer;
+ friend class Verifier;
+
+ public:
+ CryptoHash() : hash_(nullptr) {}
+
+ CryptoHash(const CryptoHash& other) {
+ if (other.hash_) {
+ hash_ = parcCryptoHash_Acquire(other.hash_);
+ }
+ }
+
+ CryptoHash(CryptoHash&& other) {
+ if (other.hash_) {
+ hash_ = parcCryptoHash_Acquire(other.hash_);
+ }
+ }
+
+ template <typename T>
+ CryptoHash(const T* buffer, std::size_t length, CryptoHashType hash_type) {
+ hash_ = parcCryptoHash_CreateFromArray(
+ static_cast<PARCCryptoHashType>(hash_type), buffer, length);
+ }
+
+ ~CryptoHash() {
+ if (hash_) {
+ parcCryptoHash_Release(&hash_);
+ }
+ }
+
+ CryptoHash& operator=(const CryptoHash& other) {
+ if (other.hash_) {
+ hash_ = parcCryptoHash_Acquire(other.hash_);
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ utils::Array<T> getDigest() const {
+ return utils::Array<T>(
+ static_cast<T*>(parcBuffer_Overlay(parcCryptoHash_GetDigest(hash_), 0)),
+ parcBuffer_Remaining(parcCryptoHash_GetDigest(hash_)));
+ }
+
+ CryptoHashType getType() {
+ return static_cast<CryptoHashType>(parcCryptoHash_GetDigestType(hash_));
+ }
+
+ template <typename T>
+ static bool compareBinaryDigest(const T* digest1, const T* digest2,
+ CryptoHashType hash_type) {
+ if (hash_size_map.find(hash_type) == hash_size_map.end()) {
+ return false;
+ }
+
+ return !static_cast<bool>(
+ std::memcmp(digest1, digest2, hash_size_map[hash_type]));
+ }
+
+ private:
+ PARCCryptoHash* hash_;
+};
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/crypto_hash_type.h b/libtransport/src/hicn/transport/utils/crypto_hash_type.h
new file mode 100755
index 000000000..b7597e208
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/crypto_hash_type.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
+
+extern "C" {
+#include <parc/security/parc_CryptoHashType.h>
+};
+
+namespace utils {
+
+enum class CryptoHashType : uint8_t {
+ SHA_256 = PARCCryptoHashType_SHA256,
+ SHA_512 = PARCCryptoHashType_SHA512,
+ CRC32C = PARCCryptoHashType_CRC32C,
+ NULL_HASH = PARCCryptoHashType_NULL
+};
+
+} \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/crypto_hasher.h b/libtransport/src/hicn/transport/utils/crypto_hasher.h
new file mode 100755
index 000000000..c34a26fac
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/crypto_hasher.h
@@ -0,0 +1,68 @@
+/*
+ * 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/crypto_hash.h>
+
+extern "C" {
+#include <parc/security/parc_CryptoHasher.h>
+};
+
+namespace utils {
+
+class CryptoHasher {
+ public:
+ CryptoHasher(CryptoHashType hash_type)
+ : hasher_(parcCryptoHasher_Create(
+ static_cast<PARCCryptoHashType>(hash_type))),
+ managed_(true) {}
+
+ CryptoHasher(PARCCryptoHasher* hasher) : hasher_(hasher), managed_(false) {}
+
+ ~CryptoHasher() {
+ if (managed_) {
+ parcCryptoHasher_Release(&hasher_);
+ }
+ }
+
+ CryptoHasher& init() {
+ if (parcCryptoHasher_Init(hasher_) == -1) {
+ throw errors::RuntimeException("Cryptohash init failed.");
+ }
+
+ return *this;
+ }
+
+ template <typename T>
+ CryptoHasher& updateBytes(const T* buffer, std::size_t length) {
+ if (parcCryptoHasher_UpdateBytes(hasher_, buffer, length) == -1) {
+ throw errors::RuntimeException("Cryptohash updateBytes failed.");
+ }
+ return *this;
+ }
+
+ CryptoHash finalize() {
+ CryptoHash hash;
+ hash.hash_ = parcCryptoHasher_Finalize(hasher_);
+ return hash;
+ }
+
+ private:
+ PARCCryptoHasher* hasher_;
+ bool managed_;
+};
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/crypto_suite.h b/libtransport/src/hicn/transport/utils/crypto_suite.h
new file mode 100755
index 000000000..8ae32b846
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/crypto_suite.h
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+extern "C" {
+#include <parc/security/parc_CryptoSuite.h>
+};
+
+namespace utils {
+
+enum class CryptoSuite : uint8_t {
+ RSA_SHA256 = PARCCryptoSuite_RSA_SHA256,
+ DSA_SHA256 = PARCCryptoSuite_DSA_SHA256,
+ RSA_SHA512 = PARCCryptoSuite_RSA_SHA512,
+ HMAC_SHA256 = PARCCryptoSuite_HMAC_SHA256,
+ HMAC_SHA512 = PARCCryptoSuite_HMAC_SHA512,
+ NULL_CRC32C = PARCCryptoSuite_NULL_CRC32C,
+ ECDSA_256K1 = PARCCryptoSuite_ECDSA_SHA256,
+ UNKNOWN = PARCCryptoSuite_UNKNOWN
+};
+
+}
diff --git a/libtransport/src/hicn/transport/utils/daemonizator.cc b/libtransport/src/hicn/transport/utils/daemonizator.cc
new file mode 100755
index 000000000..d9b3109af
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/daemonizator.cc
@@ -0,0 +1,73 @@
+/*
+ * 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/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
diff --git a/libtransport/src/hicn/transport/utils/daemonizator.h b/libtransport/src/hicn/transport/utils/daemonizator.h
new file mode 100755
index 000000000..a21ce8a7b
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/daemonizator.h
@@ -0,0 +1,25 @@
+/*
+ * 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 <cstdlib>
+namespace utils {
+
+class Daemonizator {
+ public:
+ static void daemonize(bool close_fds = true);
+};
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/deadline_timer.h b/libtransport/src/hicn/transport/utils/deadline_timer.h
new file mode 100755
index 000000000..61f906141
--- /dev/null
+++ b/libtransport/src/hicn/transport/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 <hicn/transport/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/hicn/transport/utils/endianess.h b/libtransport/src/hicn/transport/utils/endianess.h
new file mode 100755
index 000000000..a3ec21c90
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/endianess.h
@@ -0,0 +1,136 @@
+/*
+ * 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/portability/portability.h>
+
+#include <arpa/inet.h>
+#include <cstring>
+
+namespace utils {
+
+namespace {
+
+template <size_t Size>
+struct uint_types_by_size;
+
+#define GENERATOR(sz, fn) \
+ static TRANSPORT_ALWAYS_INLINE uint##sz##_t byteswap_gen(uint##sz##_t v) { \
+ return fn(v); \
+ } \
+ template <> \
+ struct uint_types_by_size<sz / 8> { \
+ using type = uint##sz##_t; \
+ };
+
+GENERATOR(8, uint8_t)
+#ifdef _MSC_VER
+GENERATOR(64, _byteswap_uint64)
+GENERATOR(32, _byteswap_ulong)
+GENERATOR(16, _byteswap_ushort)
+#else
+GENERATOR(64, __builtin_bswap64)
+GENERATOR(32, __builtin_bswap32)
+GENERATOR(16, __builtin_bswap16)
+#endif
+
+template <typename T>
+struct EndianInt {
+ static_assert(
+ (std::is_integral<T>::value && !std::is_same<T, bool>::value) ||
+ std::is_floating_point<T>::value,
+ "template type parameter must be non-bool integral or floating point");
+
+ static T swap(T x) {
+ // we implement this with memcpy because that is defined behavior in C++
+ // we rely on compilers to optimize away the memcpy calls
+ constexpr auto s = sizeof(T);
+ using B = typename uint_types_by_size<s>::type;
+ B b;
+ std::memcpy(&b, &x, s);
+ b = byteswap_gen(b);
+ std::memcpy(&x, &b, s);
+ return x;
+ }
+ static T big(T x) {
+ return portability::little_endian_arch ? EndianInt::swap(x) : x;
+ }
+ static T little(T x) {
+ return portability::big_endian_arch ? EndianInt::swap(x) : x;
+ }
+};
+
+} // namespace
+
+// big* convert between native and big-endian representations
+// little* convert between native and little-endian representations
+// swap* convert between big-endian and little-endian representations
+//
+// ntohs, htons == big16
+// ntohl, htonl == big32
+#define GENERATOR1(fn, t, sz) \
+ static t fn##sz(t x) { return fn<t>(x); }
+
+#define GENERATOR2(t, sz) \
+ GENERATOR1(swap, t, sz) \
+ GENERATOR1(big, t, sz) \
+ GENERATOR1(little, t, sz)
+
+#define GENERATOR3(sz) \
+ GENERATOR2(uint##sz##_t, sz) \
+ GENERATOR2(int##sz##_t, sz)
+
+class Endian {
+ public:
+ enum class Order : uint8_t { LITTLE, BIG };
+
+ static constexpr Order order =
+ portability::little_endian_arch ? Order::LITTLE : Order::BIG;
+
+ template <typename T>
+ static T swap(T x) {
+ return EndianInt<T>::swap(x);
+ }
+
+ template <typename T>
+ static T big(T x) {
+ return EndianInt<T>::big(x);
+ }
+
+ template <typename T>
+ static T little(T x) {
+ return EndianInt<T>::little(x);
+ }
+
+#if !defined(__ANDROID__)
+ GENERATOR3(64)
+ GENERATOR3(32)
+ GENERATOR3(16)
+ GENERATOR3(8)
+#endif
+};
+
+template <typename T>
+static TRANSPORT_ALWAYS_INLINE T ntoh(T x) {
+ return Endian::order == Endian::Order::LITTLE ? Endian::little(x) : x;
+}
+
+template <typename T>
+static TRANSPORT_ALWAYS_INLINE T hton(T x) {
+ return Endian::order == Endian::Order::LITTLE ? Endian::big(x) : x;
+}
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc b/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc
new file mode 100755
index 000000000..81b471857
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/epoll_event_reactor.cc
@@ -0,0 +1,183 @@
+/*
+ * 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 <hicn/transport/utils/epoll_event_reactor.h>
+#include <hicn/transport/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::addFileDescriptor(int fd, uint32_t events,
+ EventCallback &callback) {
+ auto it = event_callback_map_.find(fd);
+ event_callback_map_[fd] = callback;
+ if (it != event_callback_map_.end()) {
+ event_callback_map_[fd] = callback;
+ } else {
+ return addFileDescriptor(fd, events);
+ }
+
+ return 0;
+}
+
+int EpollEventReactor::addFileDescriptor(int fd, uint32_t events,
+ EventCallback &&callback) {
+ auto it = event_callback_map_.find(fd);
+ event_callback_map_[fd] = callback;
+ if (it != event_callback_map_.end()) {
+ event_callback_map_[fd] = callback;
+ } else {
+ return addFileDescriptor(fd, events);
+ }
+
+ 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;
+ }
+
+ event_callback_map_.erase(fd);
+
+ return 0;
+}
+
+void EpollEventReactor::runEventLoop(int timeout) {
+ Event evt[128];
+ int en = 0;
+
+ // 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) {
+ auto 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 {
+ event_callback_map_[evt[i].data.fd](evt[i]);
+ }
+ } else {
+ TRANSPORT_LOGE("unexpected event. fd %d", evt[i].data.fd);
+ }
+ }
+ }
+}
+
+void EpollEventReactor::runOneEvent() {
+ Event evt;
+ int en = 0;
+
+ // 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)) {
+ auto 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 {
+ event_callback_map_[evt.data.fd](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/hicn/transport/utils/epoll_event_reactor.h b/libtransport/src/hicn/transport/utils/epoll_event_reactor.h
new file mode 100755
index 000000000..bb4db3ee7
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/epoll_event_reactor.h
@@ -0,0 +1,65 @@
+/*
+ * 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/event_reactor.h>
+
+#include <sys/epoll.h>
+#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();
+
+ int addFileDescriptor(int fd, uint32_t events, EventCallback &callback);
+
+ int addFileDescriptor(int fd, uint32_t events, EventCallback &&callback);
+
+ 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_;
+ volatile bool run_event_loop_;
+ EventCallbackMap event_callback_map_;
+ std::mutex event_callback_map_mutex_;
+};
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/event_reactor.h b/libtransport/src/hicn/transport/utils/event_reactor.h
new file mode 100755
index 000000000..4f8b58296
--- /dev/null
+++ b/libtransport/src/hicn/transport/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/hicn/transport/utils/event_thread.h b/libtransport/src/hicn/transport/utils/event_thread.h
new file mode 100755
index 000000000..3bf08c94b
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/event_thread.h
@@ -0,0 +1,101 @@
+/*
+ * 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() {
+ TRANSPORT_LOGI("Stopping event thread!");
+
+ 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/hicn/transport/utils/fd_deadline_timer.h b/libtransport/src/hicn/transport/utils/fd_deadline_timer.h
new file mode 100755
index 000000000..3ed4590bc
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/fd_deadline_timer.h
@@ -0,0 +1,127 @@
+/*
+ * 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/deadline_timer.h>
+#include <hicn/transport/utils/epoll_event_reactor.h>
+#include <hicn/transport/utils/log.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{move(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/hicn/transport/utils/hash.h b/libtransport/src/hicn/transport/utils/hash.h
new file mode 100755
index 000000000..6815ca4bf
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/hash.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright 2017 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.
+ */
+
+#pragma once
+
+#include <hicn/transport/portability/portability.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace utils {
+
+namespace hash {
+
+/*
+ * Fowler / Noll / Vo (FNV) Hash
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ */
+
+const uint32_t FNV_32_HASH_START = 2166136261UL;
+const uint64_t FNV_64_HASH_START = 14695981039346656037ULL;
+
+TRANSPORT_ALWAYS_INLINE uint32_t fnv32(const char *s,
+ uint32_t hash = FNV_32_HASH_START) {
+ for (; *s; ++s) {
+ hash +=
+ (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
+ hash ^= *s;
+ }
+ return hash;
+}
+
+TRANSPORT_ALWAYS_INLINE uint32_t fnv32_buf(const void *buf, size_t n,
+ uint32_t hash = FNV_32_HASH_START) {
+ // forcing signed char, since other platforms can use unsigned
+ const signed char *char_buf = reinterpret_cast<const signed char *>(buf);
+
+ for (size_t i = 0; i < n; ++i) {
+ hash +=
+ (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
+ hash ^= char_buf[i];
+ }
+
+ return hash;
+}
+
+TRANSPORT_ALWAYS_INLINE uint32_t fnv32(const std::string &str,
+ uint32_t hash = FNV_32_HASH_START) {
+ return fnv32_buf(str.data(), str.size(), hash);
+}
+
+TRANSPORT_ALWAYS_INLINE uint64_t fnv64(const char *s,
+ uint64_t hash = FNV_64_HASH_START) {
+ for (; *s; ++s) {
+ hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) +
+ (hash << 8) + (hash << 40);
+ hash ^= *s;
+ }
+ return hash;
+}
+
+TRANSPORT_ALWAYS_INLINE uint64_t fnv64_buf(const void *buf, size_t n,
+ uint64_t hash = FNV_64_HASH_START) {
+ // forcing signed char, since other platforms can use unsigned
+ const signed char *char_buf = reinterpret_cast<const signed char *>(buf);
+
+ for (size_t i = 0; i < n; ++i) {
+ hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) +
+ (hash << 8) + (hash << 40);
+ hash ^= char_buf[i];
+ }
+ return hash;
+}
+
+TRANSPORT_ALWAYS_INLINE uint64_t fnv64(const std::string &str,
+ uint64_t hash = FNV_64_HASH_START) {
+ return fnv64_buf(str.data(), str.size(), hash);
+}
+
+} // namespace hash
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/identity.cc b/libtransport/src/hicn/transport/utils/identity.cc
new file mode 100755
index 000000000..bdf7f29f9
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/identity.cc
@@ -0,0 +1,130 @@
+/*
+ * 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/identity.h>
+
+extern "C" {
+#include <parc/security/parc_PublicKeySigner.h>
+#include <parc/security/parc_Security.h>
+}
+
+namespace utils {
+
+Identity::Identity(const std::string &keystore_name,
+ const std::string &keystore_password, CryptoSuite suite,
+ unsigned int key_length, unsigned int validity_days,
+ const std::string &subject_name) {
+ parcSecurity_Init();
+
+ bool success = parcPkcs12KeyStore_CreateFile(
+ keystore_name.c_str(), keystore_password.c_str(), subject_name.c_str(),
+ parcCryptoSuite_GetSigningAlgorithm(static_cast<PARCCryptoSuite>(suite)),
+ key_length, validity_days);
+
+ parcAssertTrue(success,
+ "parcPkcs12KeyStore_CreateFile('%s', '%s', '%s', %d, %d) failed.",
+ keystore_name.c_str(), keystore_password.c_str(),
+ subject_name.c_str(), static_cast<int>(key_length), validity_days);
+
+ PARCIdentityFile *identity_file =
+ parcIdentityFile_Create(keystore_name.c_str(), keystore_password.c_str());
+
+ identity_ =
+ parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity);
+
+ PARCSigner *signer = parcIdentity_CreateSigner(
+ identity_,
+ parcCryptoSuite_GetCryptoHash(static_cast<PARCCryptoSuite>(suite)));
+
+ signer_ = std::make_shared<Signer>(signer);
+
+ signature_length_ = parcSigner_GetSignatureSize(signer);
+
+ parcSigner_Release(&signer);
+ parcIdentityFile_Release(&identity_file);
+}
+
+Identity::Identity(const Identity &other)
+ : signer_(other.signer_),
+ hash_algorithm_(other.hash_algorithm_),
+ signature_length_(other.signature_length_) {
+ parcSecurity_Init();
+ identity_ = parcIdentity_Acquire(other.identity_);
+}
+
+Identity Identity::generateIdentity(const std::string &subject_name) {
+ std::string keystore_name = "keystore";
+ std::string keystore_password = "password";
+ std::size_t key_length = 1024;
+ unsigned int validity_days = 30;
+ CryptoSuite suite = CryptoSuite::RSA_SHA256;
+
+ return utils::Identity(keystore_name, keystore_password, suite, key_length,
+ validity_days, subject_name);
+}
+
+Identity::Identity(std::string &file_name, std::string &password,
+ transport::core::HashAlgorithm hash_algorithm)
+ : hash_algorithm_(hash_algorithm) {
+ parcSecurity_Init();
+
+ PARCIdentityFile *identity_file =
+ parcIdentityFile_Create(file_name.c_str(), password.c_str());
+
+ identity_ =
+ parcIdentity_Create(identity_file, PARCIdentityFileAsPARCIdentity);
+
+ PARCSigner *signer = parcIdentity_CreateSigner(
+ identity_, static_cast<PARCCryptoHashType>(hash_algorithm));
+
+ signer_ = std::make_shared<Signer>(signer);
+
+ signature_length_ = parcSigner_GetSignatureSize(signer);
+
+ parcSigner_Release(&signer);
+ parcIdentityFile_Release(&identity_file);
+}
+
+// Identity::Identity(Identity &&other) {
+// identity_ = parcIdentity_Acquire(other.identity_);
+//}
+
+// Identity& Identity::operator=(const Identity& other) {
+// signer_ = other.signer_;
+// hash_algorithm_ = other.hash_algorithm_;
+// signature_length_ = other.signature_length_;
+// identity_ = parcIdentity_Acquire(other.identity_);
+
+// parcSecurity_Init();
+// }
+
+Identity::~Identity() {
+ parcIdentity_Release(&identity_);
+ parcSecurity_Fini();
+}
+
+std::string Identity::getFileName() {
+ return std::string(parcIdentity_GetFileName(identity_));
+}
+
+std::string Identity::getPassword() {
+ return std::string(parcIdentity_GetPassWord(identity_));
+}
+
+Signer &Identity::getSigner() { return *signer_; }
+
+unsigned int Identity::getSignatureLength() const { return signature_length_; }
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/identity.h b/libtransport/src/hicn/transport/utils/identity.h
new file mode 100755
index 000000000..018842ee3
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/identity.h
@@ -0,0 +1,64 @@
+/*
+ * 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/core/manifest_format.h>
+#include <hicn/transport/utils/crypto_suite.h>
+#include <hicn/transport/utils/signer.h>
+
+extern "C" {
+#include <parc/security/parc_Identity.h>
+#include <parc/security/parc_IdentityFile.h>
+#include <parc/security/parc_Pkcs12KeyStore.h>
+};
+
+#include <string>
+
+namespace utils {
+
+class Identity {
+ public:
+ Identity(const std::string &keystore_name,
+ const std::string &keystore_password, CryptoSuite suite,
+ unsigned int signature_length, unsigned int validity_days,
+ const std::string &subject_name);
+
+ // No copies
+ Identity(const Identity &other);
+
+ Identity(std::string &file_name, std::string &password,
+ transport::core::HashAlgorithm hash_algorithm);
+
+ ~Identity();
+
+ static Identity generateIdentity(const std::string &subject_name);
+
+ std::string getFileName();
+
+ std::string getPassword();
+
+ Signer &getSigner();
+
+ unsigned int getSignatureLength() const;
+
+ private:
+ PARCIdentity *identity_;
+ std::shared_ptr<Signer> signer_;
+ transport::core::HashAlgorithm hash_algorithm_;
+ unsigned int signature_length_;
+};
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/key_id.h b/libtransport/src/hicn/transport/utils/key_id.h
new file mode 100755
index 000000000..d67b73d7a
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/key_id.h
@@ -0,0 +1,25 @@
+/*
+ * 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 <cstdint>
+#include <utility>
+
+namespace utils {
+
+using KeyId = std::pair<uint8_t*, uint8_t>;
+
+}
diff --git a/libtransport/src/hicn/transport/utils/linux.h b/libtransport/src/hicn/transport/utils/linux.h
new file mode 100755
index 000000000..5820528e1
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/linux.h
@@ -0,0 +1,64 @@
+/*
+ * 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
+
+#ifdef __linux__
+
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/log.h>
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <string>
+
+#define LINK_LOCAL_PREFIX 0xfe80
+
+namespace utils {
+
+static TRANSPORT_ALWAYS_INLINE int retrieveInterfaceAddress(
+ const std::string &interface_name, struct sockaddr_in6 *address) {
+ struct ifaddrs *ifap, *ifa;
+ char addr[INET6_ADDRSTRLEN];
+
+ getifaddrs(&ifap);
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family == AF_INET6 &&
+ strcmp(ifa->ifa_name, interface_name.c_str()) == 0) {
+ struct sockaddr_in6 *tmp = (struct sockaddr_in6 *)ifa->ifa_addr;
+ uint16_t prefix = 0;
+ memcpy(&prefix, tmp->sin6_addr.s6_addr, sizeof(uint16_t));
+
+ if (htons(LINK_LOCAL_PREFIX) != prefix) {
+ *address = *(struct sockaddr_in6 *)ifa->ifa_addr;
+ getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr,
+ sizeof(addr), NULL, 0, NI_NUMERICHOST);
+ TRANSPORT_LOGI("Interface: %s\tAddress: %s", ifa->ifa_name, addr);
+ }
+ }
+ }
+
+ freeifaddrs(ifap);
+
+ return 0;
+}
+
+} // namespace utils
+
+#endif // __linux__ \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/literals.h b/libtransport/src/hicn/transport/utils/literals.h
new file mode 100755
index 000000000..bd00e0a58
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/literals.h
@@ -0,0 +1,55 @@
+/*
+ * 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/portability/portability.h>
+
+#include <cstdint>
+
+TRANSPORT_ALWAYS_INLINE std::uint8_t operator"" _U8(unsigned long long value) {
+ return static_cast<std::uint8_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::uint16_t operator"" _U16(
+ unsigned long long value) {
+ return static_cast<std::uint16_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::uint32_t operator"" _U32(
+ unsigned long long value) {
+ return static_cast<std::uint32_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::uint64_t operator"" _U64(
+ unsigned long long value) {
+ return static_cast<std::uint64_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::int8_t operator"" _I8(unsigned long long value) {
+ return static_cast<std::int8_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::int16_t operator"" _I16(unsigned long long value) {
+ return static_cast<std::int16_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::int32_t operator"" _I32(unsigned long long value) {
+ return static_cast<std::int32_t>(value);
+}
+
+TRANSPORT_ALWAYS_INLINE std::int64_t operator"" _I64(unsigned long long value) {
+ return static_cast<std::int64_t>(value);
+} \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/log.cc b/libtransport/src/hicn/transport/utils/log.cc
new file mode 100755
index 000000000..064625ec0
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/log.cc
@@ -0,0 +1,1405 @@
+/*
+ * 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.
+ */
+#ifdef TRANSPORT_LOG_USE_ANDROID_LOG
+#undef TRANSPORT_LOG_USE_ANDROID_LOG
+#if defined(__ANDROID__)
+#define TRANSPORT_LOG_USE_ANDROID_LOG 1
+#else
+#define TRANSPORT_LOG_USE_ANDROID_LOG 0
+#endif
+#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 <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <hicn/transport/utils/log.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/time.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), 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/hicn/transport/utils/log.h b/libtransport/src/hicn/transport/utils/log.h
new file mode 100755
index 000000000..17e47e7df
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/log.h
@@ -0,0 +1,1057 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/* To detect incompatible changes you can define TRANSPORT_LOG_VERSION_REQUIRED
+ * to be the current value of TRANSPORT_LOG_VERSION before including this file
+ * (or via compiler command line):
+ *
+ * #define TRANSPORT_LOG_VERSION_REQUIRED 4
+ * #include <hicn/transport_log.h>
+ *
+ * Compilation will fail when included file has different version.
+ */
+#define TRANSPORT_LOG_VERSION 4
+#if defined(TRANSPORT_LOG_VERSION_REQUIRED)
+#if TRANSPORT_LOG_VERSION_REQUIRED != TRANSPORT_LOG_VERSION
+#error different transport_log version required
+#endif
+#endif
+
+/* Log level guideline:
+ * - TRANSPORT_LOG_FATAL - happened something impossible and absolutely
+ * unexpected. Process can't continue and must be terminated. Example: division
+ * by zero, unexpected modifications from other thread.
+ * - TRANSPORT_LOG_ERROR - happened something possible, but highly unexpected.
+ * The process is able to recover and continue execution. Example: out of memory
+ * (could also be FATAL if not handled properly).
+ * - TRANSPORT_LOG_WARN - happened something that *usually* should not happen
+ * and significantly changes application behavior for some period of time.
+ * Example: configuration file not found, auth error.
+ * - TRANSPORT_LOG_INFO - happened significant life cycle event or major state
+ * transition.
+ * Example: app started, user logged in.
+ * - TRANSPORT_LOG_DEBUG - minimal set of events that could help to reconstruct
+ * the execution path. Usually disabled in release builds.
+ * - TRANSPORT_LOG_VERBOSE - all other events. Usually disabled in release
+ * builds.
+ *
+ * *Ideally*, log file of debugged, well tested, production ready application
+ * should be empty or very small. Choosing a right log level is as important as
+ * providing short and self descriptive log message.
+ */
+#define TRANSPORT_LOG_VERBOSE 1
+#define TRANSPORT_LOG_DEBUG 2
+#define TRANSPORT_LOG_INFO 3
+#define TRANSPORT_LOG_WARN 4
+#define TRANSPORT_LOG_ERROR 5
+#define TRANSPORT_LOG_FATAL 6
+#define TRANSPORT_LOG_NONE 0xFF
+
+/* "Current" log level is a compile time check and has no runtime overhead. Log
+ * level that is below current log level it said to be "disabled". Otherwise,
+ * it's "enabled". Log messages that are disabled has no runtime overhead - they
+ * are converted to no-op by preprocessor and then eliminated by compiler.
+ * Current log level is configured per compilation module (.c/.cpp/.m file) by
+ * defining TRANSPORT_LOG_DEF_LEVEL or TRANSPORT_LOG_LEVEL. TRANSPORT_LOG_LEVEL
+ * has higer priority and when defined overrides value provided by
+ * TRANSPORT_LOG_DEF_LEVEL.
+ *
+ * Common practice is to define default current log level with
+ * TRANSPORT_LOG_DEF_LEVEL in build script (e.g. Makefile, CMakeLists.txt, gyp,
+ * etc.) for the entire project or target:
+ *
+ * CC_ARGS := -DTRANSPORT_LOG_DEF_LEVEL=TRANSPORT_LOG_INFO
+ *
+ * And when necessary to override it with TRANSPORT_LOG_LEVEL in .c/.cpp/.m
+ * files before including transport_log.h:
+ *
+ * #define TRANSPORT_LOG_LEVEL TRANSPORT_LOG_VERBOSE
+ * #include <hicn/transport_log.h>
+ *
+ * If both TRANSPORT_LOG_DEF_LEVEL and TRANSPORT_LOG_LEVEL are undefined, then
+ * TRANSPORT_LOG_INFO will be used for release builds (NDEBUG is defined) and
+ * TRANSPORT_LOG_DEBUG otherwise (NDEBUG is not defined).
+ */
+#if defined(TRANSPORT_LOG_LEVEL)
+#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_LEVEL
+#elif defined(TRANSPORT_LOG_DEF_LEVEL)
+#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_DEF_LEVEL
+#else
+#ifdef NDEBUG
+#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_INFO
+#else
+#define _TRANSPORT_LOG_LEVEL TRANSPORT_LOG_DEBUG
+#endif
+#endif
+
+/* "Output" log level is a runtime check. When log level is below output log
+ * level it said to be "turned off" (or just "off" for short). Otherwise it's
+ * "turned on" (or just "on"). Log levels that were "disabled" (see
+ * TRANSPORT_LOG_LEVEL and TRANSPORT_LOG_DEF_LEVEL) can't be "turned on", but
+ * "enabled" log levels could be "turned off". Only messages with log level
+ * which is "turned on" will reach output facility. All other messages will be
+ * ignored (and their arguments will not be evaluated). Output log level is a
+ * global property and configured per process using
+ * transport_log_set_output_level() function which can be called at any time.
+ *
+ * Though in some cases it could be useful to configure output log level per
+ * compilation module or per library. There are two ways to achieve that:
+ * - Define TRANSPORT_LOG_OUTPUT_LEVEL to expresion that evaluates to desired
+ * output log level.
+ * - Copy transport_log.h and transport_log.c files into your library and build
+ * it with TRANSPORT_LOG_LIBRARY_PREFIX defined to library specific prefix. See
+ * TRANSPORT_LOG_LIBRARY_PREFIX for more details.
+ *
+ * When defined, TRANSPORT_LOG_OUTPUT_LEVEL must evaluate to integral value that
+ * corresponds to desired output log level. Use it only when compilation module
+ * is required to have output log level which is different from global output
+ * log level set by transport_log_set_output_level() function. For other cases,
+ * consider defining TRANSPORT_LOG_LEVEL or using
+ * transport_log_set_output_level() function.
+ *
+ * Example:
+ *
+ * #define TRANSPORT_LOG_OUTPUT_LEVEL g_module_log_level
+ * #include <hicn/transport_log.h>
+ * static int g_module_log_level = TRANSPORT_LOG_INFO;
+ * static void foo() {
+ * TRANSPORT_LOGI("Will check g_module_log_level for output log level");
+ * }
+ * void debug_log(bool on) {
+ * g_module_log_level = on? TRANSPORT_LOG_DEBUG: TRANSPORT_LOG_INFO;
+ * }
+ *
+ * Note on performance. This expression will be evaluated each time message is
+ * logged (except when message log level is "disabled" - see TRANSPORT_LOG_LEVEL
+ * for details). Keep this expression as simple as possible, otherwise it will
+ * not only add runtime overhead, but also will increase size of call site
+ * (which will result in larger executable). The prefered way is to use integer
+ * variable (as in example above). If structure must be used, log_level field
+ * must be the first field in this structure:
+ *
+ * #define TRANSPORT_LOG_OUTPUT_LEVEL (g_config.log_level)
+ * #include <hicn/transport_log.h>
+ * struct config {
+ * int log_level;
+ * unsigned other_field;
+ * [...]
+ * };
+ * static config g_config = {TRANSPORT_LOG_INFO, 0, ...};
+ *
+ * This allows compiler to generate more compact load instruction (no need to
+ * specify offset since it's zero). Calling a function to get output log level
+ * is generaly a bad idea, since it will increase call site size and runtime
+ * overhead even further.
+ */
+#if defined(TRANSPORT_LOG_OUTPUT_LEVEL)
+#define _TRANSPORT_LOG_OUTPUT_LEVEL TRANSPORT_LOG_OUTPUT_LEVEL
+#else
+#define _TRANSPORT_LOG_OUTPUT_LEVEL _transport_log_global_output_lvl
+#endif
+
+/* "Tag" is a compound string that could be associated with a log message. It
+ * consists of tag prefix and tag (both are optional).
+ *
+ * Tag prefix is a global property and configured per process using
+ * transport_log_set_tag_prefix() function. Tag prefix identifies context in
+ * which component or module is running (e.g. process name). For example, the
+ * same library could be used in both client and server processes that work on
+ * the same machine. Tag prefix could be used to easily distinguish between
+ * them. For more details about tag prefix see transport_log_set_tag_prefix()
+ * function. Tag prefix
+ *
+ * Tag identifies component or module. It is configured per compilation module
+ * (.c/.cpp/.m file) by defining TRANSPORT_LOG_TAG or TRANSPORT_LOG_DEF_TAG.
+ * TRANSPORT_LOG_TAG has higer priority and when defined overrides value
+ * provided by TRANSPORT_LOG_DEF_TAG. When defined, value must evaluate to
+ * (const char *), so for strings double quotes must be used.
+ *
+ * Default tag could be defined with TRANSPORT_LOG_DEF_TAG in build script (e.g.
+ * Makefile, CMakeLists.txt, gyp, etc.) for the entire project or target:
+ *
+ * CC_ARGS := -DTRANSPORT_LOG_DEF_TAG=\"MISC\"
+ *
+ * And when necessary could be overriden with TRANSPORT_LOG_TAG in .c/.cpp/.m
+ * files before including transport_log.h:
+ *
+ * #define TRANSPORT_LOG_TAG "MAIN"
+ * #include <hicn/transport_log.h>
+ *
+ * If both TRANSPORT_LOG_DEF_TAG and TRANSPORT_LOG_TAG are undefined no tag will
+ * be added to the log message (tag prefix still could be added though).
+ *
+ * Output example:
+ *
+ * 04-29 22:43:20.244 40059 1299 I hello.MAIN Number of arguments: 1
+ * | |
+ * | +- tag (e.g. module)
+ * +- tag prefix (e.g. process name)
+ */
+#if defined(TRANSPORT_LOG_TAG)
+#define _TRANSPORT_LOG_TAG TRANSPORT_LOG_TAG
+#elif defined(TRANSPORT_LOG_DEF_TAG)
+#define _TRANSPORT_LOG_TAG TRANSPORT_LOG_DEF_TAG
+#else
+#define _TRANSPORT_LOG_TAG 0
+#endif
+
+/* Source location is part of a log line that describes location (function or
+ * method name, file name and line number, e.g. "runloop@main.cpp:68") of a
+ * log statement that produced it.
+ * Source location formats are:
+ * - TRANSPORT_LOG_SRCLOC_NONE - don't add source location to log line.
+ * - TRANSPORT_LOG_SRCLOC_SHORT - add source location in short form (file and
+ * line number, e.g. "@main.cpp:68").
+ * - TRANSPORT_LOG_SRCLOC_LONG - add source location in long form (function or
+ * method name, file and line number, e.g. "runloop@main.cpp:68").
+ */
+#define TRANSPORT_LOG_SRCLOC_NONE 0
+#define TRANSPORT_LOG_SRCLOC_SHORT 1
+#define TRANSPORT_LOG_SRCLOC_LONG 2
+
+/* Source location format is configured per compilation module (.c/.cpp/.m
+ * file) by defining TRANSPORT_LOG_DEF_SRCLOC or TRANSPORT_LOG_SRCLOC.
+ * TRANSPORT_LOG_SRCLOC has higer priority and when defined overrides value
+ * provided by TRANSPORT_LOG_DEF_SRCLOC.
+ *
+ * Common practice is to define default format with TRANSPORT_LOG_DEF_SRCLOC in
+ * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire
+ * project or target:
+ *
+ * CC_ARGS := -DTRANSPORT_LOG_DEF_SRCLOC=TRANSPORT_LOG_SRCLOC_LONG
+ *
+ * And when necessary to override it with TRANSPORT_LOG_SRCLOC in .c/.cpp/.m
+ * files before including transport_log.h:
+ *
+ * #define TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_NONE
+ * #include <hicn/transport_log.h>
+ *
+ * If both TRANSPORT_LOG_DEF_SRCLOC and TRANSPORT_LOG_SRCLOC are undefined, then
+ * TRANSPORT_LOG_SRCLOC_NONE will be used for release builds (NDEBUG is defined)
+ * and TRANSPORT_LOG_SRCLOC_LONG otherwise (NDEBUG is not defined).
+ */
+#if defined(TRANSPORT_LOG_SRCLOC)
+#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC
+#elif defined(TRANSPORT_LOG_DEF_SRCLOC)
+#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_DEF_SRCLOC
+#else
+#ifdef NDEBUG
+#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_NONE
+#else
+#define _TRANSPORT_LOG_SRCLOC TRANSPORT_LOG_SRCLOC_LONG
+#endif
+#endif
+#if TRANSPORT_LOG_SRCLOC_LONG == _TRANSPORT_LOG_SRCLOC
+#define _TRANSPORT_LOG_SRCLOC_FUNCTION _TRANSPORT_LOG_FUNCTION
+#else
+#define _TRANSPORT_LOG_SRCLOC_FUNCTION 0
+#endif
+
+/* Censoring provides conditional logging of secret information, also known as
+ * Personally Identifiable Information (PII) or Sensitive Personal Information
+ * (SPI). Censoring can be either enabled (TRANSPORT_LOG_CENSORED) or disabled
+ * (TRANSPORT_LOG_UNCENSORED). When censoring is enabled, log statements marked
+ * as "secrets" will be ignored and will have zero overhead (arguments also will
+ * not be evaluated).
+ */
+#define TRANSPORT_LOG_CENSORED 1
+#define TRANSPORT_LOG_UNCENSORED 0
+
+/* Censoring is configured per compilation module (.c/.cpp/.m file) by defining
+ * TRANSPORT_LOG_DEF_CENSORING or TRANSPORT_LOG_CENSORING.
+ * TRANSPORT_LOG_CENSORING has higer priority and when defined overrides value
+ * provided by TRANSPORT_LOG_DEF_CENSORING.
+ *
+ * Common practice is to define default censoring with
+ * TRANSPORT_LOG_DEF_CENSORING in build script (e.g. Makefile, CMakeLists.txt,
+ * gyp, etc.) for the entire project or target:
+ *
+ * CC_ARGS := -DTRANSPORT_LOG_DEF_CENSORING=TRANSPORT_LOG_CENSORED
+ *
+ * And when necessary to override it with TRANSPORT_LOG_CENSORING in .c/.cpp/.m
+ * files before including transport_log.h (consider doing it only for debug
+ * purposes and be very careful not to push such temporary changes to source
+ * control):
+ *
+ * #define TRANSPORT_LOG_CENSORING TRANSPORT_LOG_UNCENSORED
+ * #include <hicn/transport_log.h>
+ *
+ * If both TRANSPORT_LOG_DEF_CENSORING and TRANSPORT_LOG_CENSORING are
+ * undefined, then TRANSPORT_LOG_CENSORED will be used for release builds
+ * (NDEBUG is defined) and TRANSPORT_LOG_UNCENSORED otherwise (NDEBUG is not
+ * defined).
+ */
+#if defined(TRANSPORT_LOG_CENSORING)
+#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_CENSORING
+#elif defined(TRANSPORT_LOG_DEF_CENSORING)
+#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_DEF_CENSORING
+#else
+#ifdef NDEBUG
+#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_CENSORED
+#else
+#define _TRANSPORT_LOG_CENSORING TRANSPORT_LOG_UNCENSORED
+#endif
+#endif
+
+/* Check censoring at compile time. Evaluates to true when censoring is disabled
+ * (i.e. when secrets will be logged). For example:
+ *
+ * #if TRANSPORT_LOG_SECRETS
+ * char ssn[16];
+ * getSocialSecurityNumber(ssn);
+ * TRANSPORT_LOGI("Customer ssn: %s", ssn);
+ * #endif
+ *
+ * See TRANSPORT_LOG_SECRET() macro for a more convenient way of guarding single
+ * log statement.
+ */
+#define TRANSPORT_LOG_SECRETS \
+ (TRANSPORT_LOG_UNCENSORED == _TRANSPORT_LOG_CENSORING)
+
+/* Static (compile-time) initialization support allows to configure logging
+ * before entering main() function. This mostly useful in C++ where functions
+ * and methods could be called during initialization of global objects. Those
+ * functions and methods could record log messages too and for that reason
+ * static initialization of logging configuration is customizable.
+ *
+ * Macros below allow to specify values to use for initial configuration:
+ * - TRANSPORT_LOG_EXTERN_TAG_PREFIX - tag prefix (default: none)
+ * - TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT - global format options (default: see
+ * TRANSPORT_LOG_MEM_WIDTH in transport_log.c)
+ * - TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT - global output facility (default:
+ * stderr or platform specific, see TRANSPORT_LOG_USE_XXX macros in
+ * transport_log.c)
+ * - TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - global output log level
+ * (default: 0 - all levals are "turned on")
+ *
+ * For example, in log_config.c:
+ *
+ * #include <hicn/transport_log.h>
+ * TRANSPORT_LOG_DEFINE_TAG_PREFIX = "MyApp";
+ * TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT = {CUSTOM_MEM_WIDTH};
+ * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_PUT_STD,
+ * custom_output_callback, 0}; TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL =
+ * TRANSPORT_LOG_INFO;
+ *
+ * However, to use any of those macros transport_log library must be compiled
+ * with following macros defined:
+ * - to use TRANSPORT_LOG_DEFINE_TAG_PREFIX define
+ * TRANSPORT_LOG_EXTERN_TAG_PREFIX
+ * - to use TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT define
+ * TRANSPORT_LOG_EXTERN_GLOBAL_FORMAT
+ * - to use TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT define
+ * TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT
+ * - to use TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL define
+ * TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
+ *
+ * When transport_log library compiled with one of TRANSPORT_LOG_EXTERN_XXX
+ * macros defined, corresponding TRANSPORT_LOG_DEFINE_XXX macro MUST be used
+ * exactly once somewhere. Otherwise build will fail with link error (undefined
+ * symbol).
+ */
+#define TRANSPORT_LOG_DEFINE_TAG_PREFIX const char *_transport_log_tag_prefix
+#define TRANSPORT_LOG_DEFINE_GLOBAL_FORMAT \
+ transport_log_format _transport_log_global_format
+#define TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT \
+ transport_log_output _transport_log_global_output
+#define TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL \
+ int _transport_log_global_output_lvl
+
+/* Pointer to global format options. Direct modification is not allowed. Use
+ * transport_log_set_mem_width() instead. Could be used to initialize
+ * transport_log_spec structure:
+ *
+ * const transport_log_output g_output = {TRANSPORT_LOG_PUT_STD,
+ * output_callback, 0}; const transport_log_spec g_spec =
+ * {TRANSPORT_LOG_GLOBAL_FORMAT, &g_output}; TRANSPORT_LOGI_AUX(&g_spec,
+ * "Hello");
+ */
+#define TRANSPORT_LOG_GLOBAL_FORMAT \
+ ((const transport_log_format *)&_transport_log_global_format)
+
+/* Pointer to global output variable. Direct modification is not allowed. Use
+ * transport_log_set_output_v() or transport_log_set_output_p() instead. Could
+ * be used to initialize transport_log_spec structure:
+ *
+ * const transport_log_format g_format = {40};
+ * const transport_log_spec g_spec = {g_format, TRANSPORT_LOG_GLOBAL_OUTPUT};
+ * TRANSPORT_LOGI_AUX(&g_spec, "Hello");
+ */
+#define TRANSPORT_LOG_GLOBAL_OUTPUT \
+ ((const transport_log_output *)&_transport_log_global_output)
+
+/* When defined, all library symbols produced by linker will be prefixed with
+ * provided value. That allows to use transport_log library privately in another
+ * libraries without exposing transport_log symbols in their original form (to
+ * avoid possible conflicts with other libraries / components that also could
+ * use transport_log for logging). Value must be without quotes, for example:
+ *
+ * CC_ARGS := -DTRANSPORT_LOG_LIBRARY_PREFIX=my_lib_
+ *
+ * Note, that in this mode TRANSPORT_LOG_LIBRARY_PREFIX must be defined when
+ * building transport_log library AND it also must be defined to the same value
+ * when building a library that uses it. For example, consider fictional
+ * KittyHttp library that wants to use transport_log for logging. First approach
+ * that could be taken is to add transport_log.h and transport_log.c to the
+ * KittyHttp's source code tree directly. In that case it will be enough just to
+ * define TRANSPORT_LOG_LIBRARY_PREFIX in KittyHttp's build script:
+ *
+ * // KittyHttp/CMakeLists.txt
+ * target_compile_definitions(KittyHttp PRIVATE
+ * "TRANSPORT_LOG_LIBRARY_PREFIX=KittyHttp_")
+ *
+ * If KittyHttp doesn't want to include transport_log source code in its source
+ * tree and wants to build transport_log as a separate library than
+ * transport_log library must be built with TRANSPORT_LOG_LIBRARY_PREFIX defined
+ * to KittyHttp_ AND KittyHttp library itself also needs to define
+ * TRANSPORT_LOG_LIBRARY_PREFIX to KittyHttp_. It can do so either in its build
+ * script, as in example above, or by providing a wrapper header that KittyHttp
+ * library will need to use instead of transport_log.h:
+ *
+ * // KittyHttpLogging.h
+ * #define TRANSPORT_LOG_LIBRARY_PREFIX KittyHttp_
+ * #include <hicn/transport_log.h>
+ *
+ * Regardless of the method chosen, the end result is that transport_log symbols
+ * will be prefixed with "KittyHttp_", so if a user of KittyHttp (say
+ * DogeBrowser) also uses transport_log for logging, they will not interferer
+ * with each other. Both will have their own log level, output facility, format
+ * options etc.
+ */
+#ifdef TRANSPORT_LOG_LIBRARY_PREFIX
+#define _TRANSPORT_LOG_DECOR__(prefix, name) prefix##name
+#define _TRANSPORT_LOG_DECOR_(prefix, name) _TRANSPORT_LOG_DECOR__(prefix, name)
+#define _TRANSPORT_LOG_DECOR(name) \
+ _TRANSPORT_LOG_DECOR_(TRANSPORT_LOG_LIBRARY_PREFIX, name)
+
+#define transport_log_set_tag_prefix \
+ _TRANSPORT_LOG_DECOR(transport_log_set_tag_prefix)
+#define transport_log_set_mem_width \
+ _TRANSPORT_LOG_DECOR(transport_log_set_mem_width)
+#define transport_log_set_output_level \
+ _TRANSPORT_LOG_DECOR(transport_log_set_output_level)
+#define transport_log_set_output_v \
+ _TRANSPORT_LOG_DECOR(transport_log_set_output_v)
+#define transport_log_set_output_p \
+ _TRANSPORT_LOG_DECOR(transport_log_set_output_p)
+#define transport_log_out_stderr_callback \
+ _TRANSPORT_LOG_DECOR(transport_log_out_stderr_callback)
+#define _transport_log_tag_prefix \
+ _TRANSPORT_LOG_DECOR(_transport_log_tag_prefix)
+#define _transport_log_global_format \
+ _TRANSPORT_LOG_DECOR(_transport_log_global_format)
+#define _transport_log_global_output \
+ _TRANSPORT_LOG_DECOR(_transport_log_global_output)
+#define _transport_log_global_output_lvl \
+ _TRANSPORT_LOG_DECOR(_transport_log_global_output_lvl)
+#define _transport_log_write_d _TRANSPORT_LOG_DECOR(_transport_log_write_d)
+#define _transport_log_write_aux_d \
+ _TRANSPORT_LOG_DECOR(_transport_log_write_aux_d)
+#define _transport_log_write _TRANSPORT_LOG_DECOR(_transport_log_write)
+#define _transport_log_write_aux _TRANSPORT_LOG_DECOR(_transport_log_write_aux)
+#define _transport_log_write_mem_d \
+ _TRANSPORT_LOG_DECOR(_transport_log_write_mem_d)
+#define _transport_log_write_mem_aux_d \
+ _TRANSPORT_LOG_DECOR(_transport_log_write_mem_aux_d)
+#define _transport_log_write_mem _TRANSPORT_LOG_DECOR(_transport_log_write_mem)
+#define _transport_log_write_mem_aux \
+ _TRANSPORT_LOG_DECOR(_transport_log_write_mem_aux)
+#define _transport_log_stderr_spec \
+ _TRANSPORT_LOG_DECOR(_transport_log_stderr_spec)
+#endif
+
+#if defined(__printflike)
+#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check) \
+ __printflike(str_index, first_to_check)
+#elif defined(__GNUC__)
+#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check) \
+ __attribute__((format(__printf__, str_index, first_to_check)))
+#else
+#define _TRANSPORT_LOG_PRINTFLIKE(str_index, first_to_check)
+#endif
+
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__GNUC__)
+#define _TRANSPORT_LOG_FUNCTION __FUNCTION__
+#else
+#define _TRANSPORT_LOG_FUNCTION __func__
+#endif
+
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
+#define _TRANSPORT_LOG_INLINE __inline
+#define _TRANSPORT_LOG_IF(cond) \
+ __pragma(warning(push)) __pragma(warning(disable : 4127)) if (cond) \
+ __pragma(warning(pop))
+#define _TRANSPORT_LOG_WHILE(cond) \
+ __pragma(warning(push)) __pragma(warning(disable : 4127)) while (cond) \
+ __pragma(warning(pop))
+#else
+#define _TRANSPORT_LOG_INLINE inline
+#define _TRANSPORT_LOG_IF(cond) if (cond)
+#define _TRANSPORT_LOG_WHILE(cond) while (cond)
+#endif
+#define _TRANSPORT_LOG_NEVER _TRANSPORT_LOG_IF(0)
+#define _TRANSPORT_LOG_ONCE _TRANSPORT_LOG_WHILE(0)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Set tag prefix. Prefix will be separated from the tag with dot ('.').
+ * Use 0 or empty string to disable (default). Common use is to set it to
+ * the process (or build target) name (e.g. to separate client and server
+ * processes). Function will NOT copy provided prefix string, but will store the
+ * pointer. Hence specified prefix string must remain valid. See
+ * TRANSPORT_LOG_DEFINE_TAG_PREFIX for a way to set it before entering main()
+ * function. See TRANSPORT_LOG_TAG for more information about tag and tag
+ * prefix.
+ */
+void transport_log_set_tag_prefix(const char *const prefix);
+
+/* Set number of bytes per log line in memory (ASCII-HEX) output. Example:
+ *
+ * I hello.MAIN 4c6f72656d20697073756d20646f6c6f Lorem ipsum dolo
+ * |<- w bytes ->| |<- w chars ->|
+ *
+ * See TRANSPORT_LOGF_MEM and TRANSPORT_LOGF_MEM_AUX for more details.
+ */
+void transport_log_set_mem_width(const unsigned w);
+
+/* Set "output" log level. See TRANSPORT_LOG_LEVEL and
+ * TRANSPORT_LOG_OUTPUT_LEVEL for more info about log levels.
+ */
+void transport_log_set_output_level(const int lvl);
+
+/* Put mask is a set of flags that define what fields will be added to each
+ * log message. Default value is TRANSPORT_LOG_PUT_STD and other flags could be
+ * used to alter its behavior. See transport_log_set_output_v() for more
+ * details.
+ *
+ * Note about TRANSPORT_LOG_PUT_SRC: it will be added only in debug builds
+ * (NDEBUG is not defined).
+ */
+enum {
+ TRANSPORT_LOG_PUT_CTX = 1 << 0, /* context (time, pid, tid, log level) */
+ TRANSPORT_LOG_PUT_TAG = 1 << 1, /* tag (including tag prefix) */
+ TRANSPORT_LOG_PUT_SRC = 1 << 2, /* source location (file, line, function) */
+ TRANSPORT_LOG_PUT_MSG = 1 << 3, /* message text (formatted string) */
+ TRANSPORT_LOG_PUT_STD = 0xffff, /* everything (default) */
+};
+
+typedef struct transport_log_message {
+ int lvl; /* Log level of the message */
+ const char *tag; /* Associated tag (without tag prefix) */
+ char *buf; /* Buffer start */
+ char *e; /* Buffer end (last position where EOL with 0 could be written) */
+ char *p; /* Buffer content end (append position) */
+ char *tag_b; /* Prefixed tag start */
+ char *tag_e; /* Prefixed tag end (if != tag_b, points to msg separator) */
+ char *msg_b; /* Message start (expanded format string) */
+} transport_log_message;
+
+/* Type of output callback function. It will be called for each log line allowed
+ * by both "current" and "output" log levels ("enabled" and "turned on").
+ * Callback function is allowed to modify content of the buffers pointed by the
+ * msg, but it's not allowed to modify any of msg fields. Buffer pointed by msg
+ * is UTF-8 encoded (no BOM mark).
+ */
+typedef void (*transport_log_output_cb)(const transport_log_message *msg,
+ void *arg);
+
+/* Format options. For more details see transport_log_set_mem_width().
+ */
+typedef struct transport_log_format {
+ unsigned mem_width; /* Bytes per line in memory (ASCII-HEX) dump */
+} transport_log_format;
+
+/* Output facility.
+ */
+typedef struct transport_log_output {
+ unsigned
+ mask; /* What to put into log line buffer (see TRANSPORT_LOG_PUT_XXX) */
+ void *arg; /* User provided output callback argument */
+ transport_log_output_cb callback; /* Output callback function */
+} transport_log_output;
+
+/* Set output callback function.
+ *
+ * Mask allows to control what information will be added to the log line buffer
+ * before callback function is invoked. Default mask value is
+ * TRANSPORT_LOG_PUT_STD.
+ */
+void transport_log_set_output_v(const unsigned mask, void *const arg,
+ const transport_log_output_cb callback);
+static _TRANSPORT_LOG_INLINE void transport_log_set_output_p(
+ const transport_log_output *const output) {
+ transport_log_set_output_v(output->mask, output->arg, output->callback);
+}
+
+/* Used with _AUX macros and allows to override global format and output
+ * facility. Use TRANSPORT_LOG_GLOBAL_FORMAT and TRANSPORT_LOG_GLOBAL_OUTPUT for
+ * values from global configuration. Example:
+ *
+ * static const transport_log_output module_output = {
+ * TRANSPORT_LOG_PUT_STD, 0, custom_output_callback
+ * };
+ * static const transport_log_spec module_spec = {
+ * TRANSPORT_LOG_GLOBAL_FORMAT, &module_output
+ * };
+ * TRANSPORT_LOGI_AUX(&module_spec, "Position: %ix%i", x, y);
+ *
+ * See TRANSPORT_LOGF_AUX and TRANSPORT_LOGF_MEM_AUX for details.
+ */
+typedef struct transport_log_spec {
+ const transport_log_format *format;
+ const transport_log_output *output;
+} transport_log_spec;
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Execute log statement if condition is true. Example:
+ *
+ * TRANSPORT_LOG_IF(1 < 2, TRANSPORT_LOGI("Log this"));
+ * TRANSPORT_LOG_IF(1 > 2, TRANSPORT_LOGI("Don't log this"));
+ *
+ * Keep in mind though, that if condition can't be evaluated at compile time,
+ * then it will be evaluated at run time. This will increase exectuable size
+ * and can have noticeable performance overhead. Try to limit conditions to
+ * expressions that can be evaluated at compile time.
+ */
+#define TRANSPORT_LOG_IF(cond, f) \
+ do { \
+ _TRANSPORT_LOG_IF((cond)) { f; } \
+ } \
+ _TRANSPORT_LOG_ONCE
+
+/* Mark log statement as "secret". Log statements that are marked as secrets
+ * will NOT be executed when censoring is enabled (see TRANSPORT_LOG_CENSORED).
+ * Example:
+ *
+ * TRANSPORT_LOG_SECRET(TRANSPORT_LOGI("Credit card: %s", credit_card));
+ * TRANSPORT_LOG_SECRET(TRANSPORT_LOGD_MEM(cipher, cipher_sz, "Cipher
+ * bytes:"));
+ */
+#define TRANSPORT_LOG_SECRET(f) TRANSPORT_LOG_IF(TRANSPORT_LOG_SECRETS, f)
+
+/* Check "current" log level at compile time (ignoring "output" log level).
+ * Evaluates to true when specified log level is enabled. For example:
+ *
+ * #if TRANSPORT_LOG_ENABLED_DEBUG
+ * const char *const g_enum_strings[] = {
+ * "enum_value_0", "enum_value_1", "enum_value_2"
+ * };
+ * #endif
+ * // ...
+ * #if TRANSPORT_LOG_ENABLED_DEBUG
+ * TRANSPORT_LOGD("enum value: %s", g_enum_strings[v]);
+ * #endif
+ *
+ * See TRANSPORT_LOG_LEVEL for details.
+ */
+#define TRANSPORT_LOG_ENABLED(lvl) ((lvl) >= _TRANSPORT_LOG_LEVEL)
+#define TRANSPORT_LOG_ENABLED_VERBOSE \
+ TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_VERBOSE)
+#define TRANSPORT_LOG_ENABLED_DEBUG TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_DEBUG)
+#define TRANSPORT_LOG_ENABLED_INFO TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_INFO)
+#define TRANSPORT_LOG_ENABLED_WARN TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_WARN)
+#define TRANSPORT_LOG_ENABLED_ERROR TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_ERROR)
+#define TRANSPORT_LOG_ENABLED_FATAL TRANSPORT_LOG_ENABLED(TRANSPORT_LOG_FATAL)
+
+/* Check "output" log level at run time (taking into account "current" log
+ * level as well). Evaluates to true when specified log level is turned on AND
+ * enabled. For example:
+ *
+ * if (TRANSPORT_LOG_ON_DEBUG)
+ * {
+ * char hash[65];
+ * sha256(data_ptr, data_sz, hash);
+ * TRANSPORT_LOGD("data: len=%u, sha256=%s", data_sz, hash);
+ * }
+ *
+ * See TRANSPORT_LOG_OUTPUT_LEVEL for details.
+ */
+#define TRANSPORT_LOG_ON(lvl) \
+ (TRANSPORT_LOG_ENABLED((lvl)) && (lvl) >= _TRANSPORT_LOG_OUTPUT_LEVEL)
+#define TRANSPORT_LOG_ON_VERBOSE TRANSPORT_LOG_ON(TRANSPORT_LOG_VERBOSE)
+#define TRANSPORT_LOG_ON_DEBUG TRANSPORT_LOG_ON(TRANSPORT_LOG_DEBUG)
+#define TRANSPORT_LOG_ON_INFO TRANSPORT_LOG_ON(TRANSPORT_LOG_INFO)
+#define TRANSPORT_LOG_ON_WARN TRANSPORT_LOG_ON(TRANSPORT_LOG_WARN)
+#define TRANSPORT_LOG_ON_ERROR TRANSPORT_LOG_ON(TRANSPORT_LOG_ERROR)
+#define TRANSPORT_LOG_ON_FATAL TRANSPORT_LOG_ON(TRANSPORT_LOG_FATAL)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const char *_transport_log_tag_prefix;
+extern transport_log_format _transport_log_global_format;
+extern transport_log_output _transport_log_global_output;
+extern int _transport_log_global_output_lvl;
+extern const transport_log_spec _transport_log_stderr_spec;
+
+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, ...)
+ _TRANSPORT_LOG_PRINTFLIKE(6, 7);
+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, ...)
+ _TRANSPORT_LOG_PRINTFLIKE(7, 8);
+void _transport_log_write(const int lvl, const char *const tag,
+ const char *const fmt, ...)
+ _TRANSPORT_LOG_PRINTFLIKE(3, 4);
+void _transport_log_write_aux(const transport_log_spec *const log,
+ const int lvl, const char *const tag,
+ const char *const fmt, ...)
+ _TRANSPORT_LOG_PRINTFLIKE(4, 5);
+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, ...)
+ _TRANSPORT_LOG_PRINTFLIKE(8, 9);
+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, ...)
+ _TRANSPORT_LOG_PRINTFLIKE(9, 10);
+void _transport_log_write_mem(const int lvl, const char *const tag,
+ const void *const d, const unsigned d_sz,
+ const char *const fmt, ...)
+ _TRANSPORT_LOG_PRINTFLIKE(5, 6);
+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, ...)
+ _TRANSPORT_LOG_PRINTFLIKE(6, 7);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Message logging macros:
+ * - TRANSPORT_LOGV("format string", args, ...)
+ * - TRANSPORT_LOGD("format string", args, ...)
+ * - TRANSPORT_LOGI("format string", args, ...)
+ * - TRANSPORT_LOGW("format string", args, ...)
+ * - TRANSPORT_LOGE("format string", args, ...)
+ * - TRANSPORT_LOGF("format string", args, ...)
+ *
+ * Memory logging macros:
+ * - TRANSPORT_LOGV_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - TRANSPORT_LOGD_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - TRANSPORT_LOGI_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - TRANSPORT_LOGW_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - TRANSPORT_LOGE_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - TRANSPORT_LOGF_MEM(data_ptr, data_sz, "format string", args, ...)
+ *
+ * Auxiliary logging macros:
+ * - TRANSPORT_LOGV_AUX(&log_instance, "format string", args, ...)
+ * - TRANSPORT_LOGD_AUX(&log_instance, "format string", args, ...)
+ * - TRANSPORT_LOGI_AUX(&log_instance, "format string", args, ...)
+ * - TRANSPORT_LOGW_AUX(&log_instance, "format string", args, ...)
+ * - TRANSPORT_LOGE_AUX(&log_instance, "format string", args, ...)
+ * - TRANSPORT_LOGF_AUX(&log_instance, "format string", args, ...)
+ *
+ * Auxiliary memory logging macros:
+ * - TRANSPORT_LOGV_MEM_AUX(&log_instance, data_ptr, data_sz, "format string",
+ * args, ...)
+ * - TRANSPORT_LOGD_MEM_AUX(&log_instance, data_ptr, data_sz, "format string",
+ * args, ...)
+ * - TRANSPORT_LOGI_MEM_AUX(&log_instance, data_ptr, data_sz, "format string",
+ * args, ...)
+ * - TRANSPORT_LOGW_MEM_AUX(&log_instance, data_ptr, data_sz, "format string",
+ * args, ...)
+ * - TRANSPORT_LOGE_MEM_AUX(&log_instance, data_ptr, data_sz, "format string",
+ * args, ...)
+ * - TRANSPORT_LOGF_MEM_AUX(&log_instance, data_ptr, data_sz, "format string",
+ * args, ...)
+ *
+ * Preformatted string logging macros:
+ * - TRANSPORT_LOGV_STR("preformatted string");
+ * - TRANSPORT_LOGD_STR("preformatted string");
+ * - TRANSPORT_LOGI_STR("preformatted string");
+ * - TRANSPORT_LOGW_STR("preformatted string");
+ * - TRANSPORT_LOGE_STR("preformatted string");
+ * - TRANSPORT_LOGF_STR("preformatted string");
+ *
+ * Explicit log level and tag macros:
+ * - TRANSPORT_LOG_WRITE(level, tag, "format string", args, ...)
+ * - TRANSPORT_LOG_WRITE_MEM(level, tag, data_ptr, data_sz, "format string",
+ * args, ...)
+ * - TRANSPORT_LOG_WRITE_AUX(&log_instance, level, tag, "format string", args,
+ * ...)
+ * - TRANSPORT_LOG_WRITE_MEM_AUX(&log_instance, level, tag, data_ptr, data_sz,
+ * "format string", args, ...)
+ *
+ * Format string follows printf() conventions. Both data_ptr and data_sz could
+ * be 0. Tag can be 0 as well. Most compilers will verify that type of arguments
+ * match format specifiers in format string.
+ *
+ * Library assuming UTF-8 encoding for all strings (char *), including format
+ * string itself.
+ */
+#if TRANSPORT_LOG_SRCLOC_NONE == _TRANSPORT_LOG_SRCLOC
+#define TRANSPORT_LOG_WRITE(lvl, tag, ...) \
+ do { \
+ if (TRANSPORT_LOG_ON(lvl)) _transport_log_write(lvl, tag, __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+#define TRANSPORT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \
+ do { \
+ if (TRANSPORT_LOG_ON(lvl)) \
+ _transport_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+#define TRANSPORT_LOG_WRITE_AUX(log, lvl, tag, ...) \
+ do { \
+ if (TRANSPORT_LOG_ON(lvl)) \
+ _transport_log_write_aux(log, lvl, tag, __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+#define TRANSPORT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \
+ do { \
+ if (TRANSPORT_LOG_ON(lvl)) \
+ _transport_log_write_mem_aux(log, lvl, tag, d, d_sz, __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+#else
+#define TRANSPORT_LOG_WRITE(lvl, tag, ...) \
+ do { \
+ if (TRANSPORT_LOG_ON(lvl)) \
+ _transport_log_write_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \
+ __LINE__, lvl, tag, __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+#define TRANSPORT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \
+ do { \
+ if (TRANSPORT_LOG_ON(lvl)) \
+ _transport_log_write_mem_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \
+ __LINE__, lvl, tag, d, d_sz, __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+#define TRANSPORT_LOG_WRITE_AUX(log, lvl, tag, ...) \
+ do { \
+ if (TRANSPORT_LOG_ON(lvl)) \
+ _transport_log_write_aux_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \
+ __LINE__, log, lvl, tag, __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+#define TRANSPORT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \
+ do { \
+ if (TRANSPORT_LOG_ON(lvl)) \
+ _transport_log_write_mem_aux_d(_TRANSPORT_LOG_SRCLOC_FUNCTION, __FILE__, \
+ __LINE__, log, lvl, tag, d, d_sz, \
+ __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+#endif
+
+static _TRANSPORT_LOG_INLINE void _transport_log_unused(const int dummy, ...) {
+ (void)dummy;
+}
+
+#define _TRANSPORT_LOG_UNUSED(...) \
+ do { \
+ _TRANSPORT_LOG_NEVER _transport_log_unused(0, __VA_ARGS__); \
+ } \
+ _TRANSPORT_LOG_ONCE
+
+#if TRANSPORT_LOG_ENABLED_VERBOSE
+#define TRANSPORT_LOGV(...) \
+ TRANSPORT_LOG_WRITE(TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, __VA_ARGS__)
+#define TRANSPORT_LOGV_AUX(log, ...) \
+ TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGV_MEM(d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, d, d_sz, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGV_MEM_AUX(log, d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM(log, TRANSPORT_LOG_VERBOSE, _TRANSPORT_LOG_TAG, d, \
+ d_sz, __VA_ARGS__)
+#else
+#define TRANSPORT_LOGV(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGV_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGV_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGV_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if TRANSPORT_LOG_ENABLED_DEBUG
+#define TRANSPORT_LOGD(...) \
+ TRANSPORT_LOG_WRITE(TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, __VA_ARGS__)
+#define TRANSPORT_LOGD_AUX(log, ...) \
+ TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGD_MEM(d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, d, d_sz, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGD_MEM_AUX(log, d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_DEBUG, _TRANSPORT_LOG_TAG, d, \
+ d_sz, __VA_ARGS__)
+#else
+#define TRANSPORT_LOGD(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGD_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGD_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGD_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if TRANSPORT_LOG_ENABLED_INFO
+#define TRANSPORT_LOGI(...) \
+ TRANSPORT_LOG_WRITE(TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, __VA_ARGS__)
+#define TRANSPORT_LOGI_AUX(log, ...) \
+ TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGI_MEM(d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, d, d_sz, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGI_MEM_AUX(log, d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_INFO, _TRANSPORT_LOG_TAG, d, \
+ d_sz, __VA_ARGS__)
+#else
+#define TRANSPORT_LOGI(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGI_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGI_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGI_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if TRANSPORT_LOG_ENABLED_WARN
+#define TRANSPORT_LOGW(...) \
+ TRANSPORT_LOG_WRITE(TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, __VA_ARGS__)
+#define TRANSPORT_LOGW_AUX(log, ...) \
+ TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGW_MEM(d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, d, d_sz, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGW_MEM_AUX(log, d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_WARN, _TRANSPORT_LOG_TAG, d, \
+ d_sz, __VA_ARGS__)
+#else
+#define TRANSPORT_LOGW(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGW_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGW_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGW_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if TRANSPORT_LOG_ENABLED_ERROR
+#define TRANSPORT_LOGE(...) \
+ TRANSPORT_LOG_WRITE(TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, __VA_ARGS__)
+#define TRANSPORT_LOGE_AUX(log, ...) \
+ TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGE_MEM(d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, d, d_sz, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGE_MEM_AUX(log, d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_ERROR, _TRANSPORT_LOG_TAG, d, \
+ d_sz, __VA_ARGS__)
+#else
+#define TRANSPORT_LOGE(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGE_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGE_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGE_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if TRANSPORT_LOG_ENABLED_FATAL
+#define TRANSPORT_LOGF(...) \
+ TRANSPORT_LOG_WRITE(TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, __VA_ARGS__)
+#define TRANSPORT_LOGF_AUX(log, ...) \
+ TRANSPORT_LOG_WRITE_AUX(log, TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGF_MEM(d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM(TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, d, d_sz, \
+ __VA_ARGS__)
+#define TRANSPORT_LOGF_MEM_AUX(log, d, d_sz, ...) \
+ TRANSPORT_LOG_WRITE_MEM_AUX(log, TRANSPORT_LOG_FATAL, _TRANSPORT_LOG_TAG, d, \
+ d_sz, __VA_ARGS__)
+#else
+#define TRANSPORT_LOGF(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGF_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGF_MEM(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#define TRANSPORT_LOGF_MEM_AUX(...) _TRANSPORT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#define TRANSPORT_LOGV_STR(s) TRANSPORT_LOGV("%s", (s))
+#define TRANSPORT_LOGD_STR(s) TRANSPORT_LOGD("%s", (s))
+#define TRANSPORT_LOGI_STR(s) TRANSPORT_LOGI("%s", (s))
+#define TRANSPORT_LOGW_STR(s) TRANSPORT_LOGW("%s", (s))
+#define TRANSPORT_LOGE_STR(s) TRANSPORT_LOGE("%s", (s))
+#define TRANSPORT_LOGF_STR(s) TRANSPORT_LOGF("%s", (s))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Output to standard error stream. Library uses it by default, though in few
+ * cases it could be necessary to specify it explicitly. For example, when
+ * transport_log library is compiled with TRANSPORT_LOG_EXTERN_GLOBAL_OUTPUT,
+ * application must define and initialize global output variable:
+ *
+ * TRANSPORT_LOG_DEFINE_GLOBAL_OUTPUT = {TRANSPORT_LOG_OUT_STDERR};
+ *
+ * Another example is when using custom output, stderr could be used as a
+ * fallback when custom output facility failed to initialize:
+ *
+ * transport_log_set_output_v(TRANSPORT_LOG_OUT_STDERR);
+ */
+enum { TRANSPORT_LOG_OUT_STDERR_MASK = TRANSPORT_LOG_PUT_STD };
+void transport_log_out_stderr_callback(const transport_log_message *const msg,
+ void *arg);
+#define TRANSPORT_LOG_OUT_STDERR \
+ TRANSPORT_LOG_OUT_STDERR_MASK, 0, transport_log_out_stderr_callback
+
+/* Predefined spec for stderr. Uses global format options
+ * (TRANSPORT_LOG_GLOBAL_FORMAT) and TRANSPORT_LOG_OUT_STDERR. Could be used to
+ * force output to stderr for a particular message. Example:
+ *
+ * f = fopen("foo.log", "w");
+ * if (!f)
+ * TRANSPORT_LOGE_AUX(TRANSPORT_LOG_STDERR, "Failed to open log file");
+ */
+#define TRANSPORT_LOG_STDERR (&_transport_log_stderr_spec)
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/membuf.cc b/libtransport/src/hicn/transport/utils/membuf.cc
new file mode 100755
index 000000000..0ab1a6044
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/membuf.cc
@@ -0,0 +1,864 @@
+/*
+ * 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
+ */
+
+#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/hicn/transport/utils/membuf.h b/libtransport/src/hicn/transport/utils/membuf.h
new file mode 100755
index 000000000..944237e2b
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/membuf.h
@@ -0,0 +1,916 @@
+/*
+ * 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
+ */
+
+#pragma once
+
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/branch_prediction.h>
+
+#include <atomic>
+#include <cassert>
+#include <cinttypes>
+#include <cstddef>
+#include <cstring>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+// Ignore shadowing warnings within this file, so includers can use -Wshadow.
+TRANSPORT_GNU_DISABLE_WARNING("-Wshadow")
+
+namespace utils {
+
+class MemBuf {
+ public:
+ enum CreateOp { CREATE };
+ enum WrapBufferOp { WRAP_BUFFER };
+ enum TakeOwnershipOp { TAKE_OWNERSHIP };
+ enum CopyBufferOp { COPY_BUFFER };
+
+ typedef void (*FreeFunction)(void* buf, void* userData);
+
+ static std::unique_ptr<MemBuf> create(std::size_t capacity);
+ MemBuf(CreateOp, std::size_t capacity);
+
+ /**
+ * Create a new MemBuf, using a single memory allocation to allocate space
+ * for both the MemBuf object and the data storage space.
+ *
+ * This saves one memory allocation. However, it can be wasteful if you
+ * later need to grow the buffer using reserve(). If the buffer needs to be
+ * reallocated, the space originally allocated will not be freed() until the
+ * MemBuf object itself is also freed. (It can also be slightly wasteful in
+ * some cases where you clone this MemBuf and then free the original MemBuf.)
+ */
+ static std::unique_ptr<MemBuf> createCombined(std::size_t capacity);
+
+ /**
+ * Create a new IOBuf, using separate memory allocations for the IOBuf object
+ * for the IOBuf and the data storage space.
+ *
+ * This requires two memory allocations, but saves space in the long run
+ * if you know that you will need to reallocate the data buffer later.
+ */
+ static std::unique_ptr<MemBuf> createSeparate(std::size_t capacity);
+
+ /**
+ * Allocate a new MemBuf chain with the requested total capacity, allocating
+ * no more than maxBufCapacity to each buffer.
+ */
+ static std::unique_ptr<MemBuf> createChain(size_t totalCapacity,
+ std::size_t maxBufCapacity);
+
+ static std::unique_ptr<MemBuf> takeOwnership(void* buf, std::size_t capacity,
+ FreeFunction freeFn = nullptr,
+ void* userData = nullptr,
+ bool freeOnError = true) {
+ return takeOwnership(buf, capacity, capacity, freeFn, userData,
+ freeOnError);
+ }
+
+ MemBuf(TakeOwnershipOp op, void* buf, std::size_t capacity,
+ FreeFunction freeFn = nullptr, void* userData = nullptr,
+ bool freeOnError = true)
+ : MemBuf(op, buf, capacity, capacity, freeFn, userData, freeOnError) {}
+
+ static std::unique_ptr<MemBuf> takeOwnership(void* buf, std::size_t capacity,
+ std::size_t length,
+ FreeFunction freeFn = nullptr,
+ void* userData = nullptr,
+ bool freeOnError = true);
+
+ MemBuf(TakeOwnershipOp, void* buf, std::size_t capacity, std::size_t length,
+ FreeFunction freeFn = nullptr, void* userData = nullptr,
+ bool freeOnError = true);
+
+ static std::unique_ptr<MemBuf> wrapBuffer(const void* buf,
+ std::size_t capacity);
+
+ static MemBuf wrapBufferAsValue(const void* buf,
+ std::size_t capacity) noexcept;
+
+ MemBuf(WrapBufferOp op, const void* buf, std::size_t capacity) noexcept;
+
+ /**
+ * Convenience function to create a new MemBuf object that copies data from a
+ * user-supplied buffer, optionally allocating a given amount of
+ * headroom and tailroom.
+ */
+ static std::unique_ptr<MemBuf> copyBuffer(const void* buf, std::size_t size,
+ std::size_t headroom = 0,
+ std::size_t minTailroom = 0);
+
+ MemBuf(CopyBufferOp op, const void* buf, std::size_t size,
+ std::size_t headroom = 0, std::size_t minTailroom = 0);
+
+ /**
+ * Convenience function to free a chain of MemBufs held by a unique_ptr.
+ */
+ static void destroy(std::unique_ptr<MemBuf>&& data) {
+ auto destroyer = std::move(data);
+ }
+
+ ~MemBuf();
+
+ bool empty() const;
+
+ const uint8_t* data() const { return data_; }
+
+ uint8_t* writableData() { return data_; }
+
+ const uint8_t* tail() const { return data_ + length_; }
+
+ uint8_t* writableTail() { return data_ + length_; }
+
+ std::size_t length() const { return length_; }
+
+ std::size_t headroom() const { return std::size_t(data_ - buffer()); }
+
+ std::size_t tailroom() const { return std::size_t(bufferEnd() - tail()); }
+
+ const uint8_t* buffer() const { return buf_; }
+
+ uint8_t* writableBuffer() { return buf_; }
+
+ const uint8_t* bufferEnd() const { return buf_ + capacity_; }
+
+ std::size_t capacity() const { return capacity_; }
+
+ MemBuf* next() { return next_; }
+
+ const MemBuf* next() const { return next_; }
+
+ MemBuf* prev() { return prev_; }
+
+ const MemBuf* prev() const { return prev_; }
+
+ /**
+ * Shift the data forwards in the buffer.
+ *
+ * This shifts the data pointer forwards in the buffer to increase the
+ * headroom. This is commonly used to increase the headroom in a newly
+ * allocated buffer.
+ *
+ * The caller is responsible for ensuring that there is sufficient
+ * tailroom in the buffer before calling advance().
+ *
+ * If there is a non-zero data length, advance() will use memmove() to shift
+ * the data forwards in the buffer. In this case, the caller is responsible
+ * for making sure the buffer is unshared, so it will not affect other MemBufs
+ * that may be sharing the same underlying buffer.
+ */
+ void advance(std::size_t amount) {
+ // In debug builds, assert if there is a problem.
+ assert(amount <= tailroom());
+
+ if (length_ > 0) {
+ memmove(data_ + amount, data_, length_);
+ }
+ data_ += amount;
+ }
+
+ /**
+ * Shift the data backwards in the buffer.
+ *
+ * The caller is responsible for ensuring that there is sufficient headroom
+ * in the buffer before calling retreat().
+ *
+ * If there is a non-zero data length, retreat() will use memmove() to shift
+ * the data backwards in the buffer. In this case, the caller is responsible
+ * for making sure the buffer is unshared, so it will not affect other MemBufs
+ * that may be sharing the same underlying buffer.
+ */
+ void retreat(std::size_t amount) {
+ // In debug builds, assert if there is a problem.
+ assert(amount <= headroom());
+
+ if (length_ > 0) {
+ memmove(data_ - amount, data_, length_);
+ }
+ data_ -= amount;
+ }
+
+ void prepend(std::size_t amount) {
+ data_ -= amount;
+ length_ += amount;
+ }
+
+ void append(std::size_t amount) { length_ += amount; }
+
+ void trimStart(std::size_t amount) {
+ data_ += amount;
+ length_ -= amount;
+ }
+
+ void trimEnd(std::size_t amount) { length_ -= amount; }
+
+ void clear() {
+ data_ = writableBuffer();
+ length_ = 0;
+ }
+
+ void reserve(std::size_t minHeadroom, std::size_t minTailroom) {
+ // Maybe we don't need to do anything.
+ if (headroom() >= minHeadroom && tailroom() >= minTailroom) {
+ return;
+ }
+ // If the buffer is empty but we have enough total room (head + tail),
+ // move the data_ pointer around.
+ if (length() == 0 && headroom() + tailroom() >= minHeadroom + minTailroom) {
+ data_ = writableBuffer() + minHeadroom;
+ return;
+ }
+ // Bah, we have to do actual work.
+ reserveSlow(minHeadroom, minTailroom);
+ }
+
+ bool isChained() const {
+ assert((next_ == this) == (prev_ == this));
+ return next_ != this;
+ }
+
+ size_t countChainElements() const;
+
+ std::size_t computeChainDataLength() const;
+
+ void prependChain(std::unique_ptr<MemBuf>&& iobuf);
+
+ void appendChain(std::unique_ptr<MemBuf>&& iobuf) {
+ // Just use prependChain() on the next element in our chain
+ next_->prependChain(std::move(iobuf));
+ }
+
+ std::unique_ptr<MemBuf> unlink() {
+ next_->prev_ = prev_;
+ prev_->next_ = next_;
+ prev_ = this;
+ next_ = this;
+ return std::unique_ptr<MemBuf>(this);
+ }
+
+ /**
+ * Remove this MemBuf from its current chain and return a unique_ptr to
+ * the MemBuf that formerly followed it in the chain.
+ */
+ std::unique_ptr<MemBuf> pop() {
+ MemBuf* next = next_;
+ next_->prev_ = prev_;
+ prev_->next_ = next_;
+ prev_ = this;
+ next_ = this;
+ return std::unique_ptr<MemBuf>((next == this) ? nullptr : next);
+ }
+
+ /**
+ * Remove a subchain from this chain.
+ *
+ * Remove the subchain starting at head and ending at tail from this chain.
+ *
+ * Returns a unique_ptr pointing to head. (In other words, ownership of the
+ * head of the subchain is transferred to the caller.) If the caller ignores
+ * the return value and lets the unique_ptr be destroyed, the subchain will
+ * be immediately destroyed.
+ *
+ * The subchain referenced by the specified head and tail must be part of the
+ * same chain as the current MemBuf, but must not contain the current MemBuf.
+ * However, the specified head and tail may be equal to each other (i.e.,
+ * they may be a subchain of length 1).
+ */
+ std::unique_ptr<MemBuf> separateChain(MemBuf* head, MemBuf* tail) {
+ assert(head != this);
+ assert(tail != this);
+
+ head->prev_->next_ = tail->next_;
+ tail->next_->prev_ = head->prev_;
+
+ head->prev_ = tail;
+ tail->next_ = head;
+
+ return std::unique_ptr<MemBuf>(head);
+ }
+
+ /**
+ * Return true if at least one of the MemBufs in this chain are shared,
+ * or false if all of the MemBufs point to unique buffers.
+ *
+ * Use isSharedOne() to only check this MemBuf rather than the entire chain.
+ */
+ bool isShared() const {
+ const MemBuf* current = this;
+ while (true) {
+ if (current->isSharedOne()) {
+ return true;
+ }
+ current = current->next_;
+ if (current == this) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Return true if all MemBufs in this chain are managed by the usual
+ * refcounting mechanism (and so the lifetime of the underlying memory
+ * can be extended by clone()).
+ */
+ bool isManaged() const {
+ const MemBuf* current = this;
+ while (true) {
+ if (!current->isManagedOne()) {
+ return false;
+ }
+ current = current->next_;
+ if (current == this) {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Return true if this MemBuf is managed by the usual refcounting mechanism
+ * (and so the lifetime of the underlying memory can be extended by
+ * cloneOne()).
+ */
+ bool isManagedOne() const { return sharedInfo(); }
+
+ /**
+ * Return true if other MemBufs are also pointing to the buffer used by this
+ * MemBuf, and false otherwise.
+ *
+ * If this MemBuf points at a buffer owned by another (non-MemBuf) part of the
+ * code (i.e., if the MemBuf was created using wrapBuffer(), or was cloned
+ * from such an MemBuf), it is always considered shared.
+ *
+ * This only checks the current MemBuf, and not other MemBufs in the chain.
+ */
+ bool isSharedOne() const {
+ // If this is a user-owned buffer, it is always considered shared
+ if ((TRANSPORT_EXPECT_FALSE(!sharedInfo()))) {
+ return true;
+ }
+
+ if ((TRANSPORT_EXPECT_FALSE(sharedInfo()->externallyShared))) {
+ return true;
+ }
+
+ if ((TRANSPORT_EXPECT_TRUE(!(flags() & flag_maybe_shared)))) {
+ return false;
+ }
+
+ // flag_maybe_shared is set, so we need to check the reference count.
+ // (Checking the reference count requires an atomic operation, which is why
+ // we prefer to only check flag_maybe_shared if possible.)
+ bool shared = sharedInfo()->refcount.load(std::memory_order_acquire) > 1;
+ if (!shared) {
+ // we're the last one left
+ clearFlags(flag_maybe_shared);
+ }
+ return shared;
+ }
+
+ /**
+ * Ensure that this MemBuf has a unique buffer that is not shared by other
+ * MemBufs.
+ *
+ * unshare() operates on an entire chain of MemBuf objects. If the chain is
+ * shared, it may also coalesce the chain when making it unique. If the
+ * chain is coalesced, subsequent MemBuf objects in the current chain will be
+ * automatically deleted.
+ *
+ * Note that buffers owned by other (non-MemBuf) users are automatically
+ * considered shared.
+ *
+ * Throws std::bad_alloc on error. On error the MemBuf chain will be
+ * unmodified.
+ *
+ * Currently unshare may also throw std::overflow_error if it tries to
+ * coalesce. (TODO: In the future it would be nice if unshare() were smart
+ * enough not to coalesce the entire buffer if the data is too large.
+ * However, in practice this seems unlikely to become an issue.)
+ */
+ void unshare() {
+ if (isChained()) {
+ unshareChained();
+ } else {
+ unshareOne();
+ }
+ }
+
+ /**
+ * Ensure that this MemBuf has a unique buffer that is not shared by other
+ * MemBufs.
+ *
+ * unshareOne() operates on a single MemBuf object. This MemBuf will have a
+ * unique buffer after unshareOne() returns, but other MemBufs in the chain
+ * may still be shared after unshareOne() returns.
+ *
+ * Throws std::bad_alloc on error. On error the MemBuf will be unmodified.
+ */
+ void unshareOne() {
+ if (isSharedOne()) {
+ unshareOneSlow();
+ }
+ }
+
+ /**
+ * Mark the underlying buffers in this chain as shared with external memory
+ * management mechanism. This will make isShared() always returns true.
+ *
+ * This function is not thread-safe, and only safe to call immediately after
+ * creating an MemBuf, before it has been shared with other threads.
+ */
+ void markExternallyShared();
+
+ /**
+ * Mark the underlying buffer that this MemBuf refers to as shared with
+ * external memory management mechanism. This will make isSharedOne() always
+ * returns true.
+ *
+ * This function is not thread-safe, and only safe to call immediately after
+ * creating an MemBuf, before it has been shared with other threads.
+ */
+ void markExternallySharedOne() {
+ SharedInfo* info = sharedInfo();
+ if (info) {
+ info->externallyShared = true;
+ }
+ }
+
+ /**
+ * Ensure that the memory that MemBufs in this chain refer to will continue to
+ * be allocated for as long as the MemBufs of the chain (or any clone()s
+ * created from this point onwards) is alive.
+ *
+ * This only has an effect for user-owned buffers (created with the
+ * WRAP_BUFFER constructor or wrapBuffer factory function), in which case
+ * those buffers are unshared.
+ */
+ void makeManaged() {
+ if (isChained()) {
+ makeManagedChained();
+ } else {
+ makeManagedOne();
+ }
+ }
+
+ /**
+ * Ensure that the memory that this MemBuf refers to will continue to be
+ * allocated for as long as this MemBuf (or any clone()s created from this
+ * point onwards) is alive.
+ *
+ * This only has an effect for user-owned buffers (created with the
+ * WRAP_BUFFER constructor or wrapBuffer factory function), in which case
+ * those buffers are unshared.
+ */
+ void makeManagedOne() {
+ if (!isManagedOne()) {
+ // We can call the internal function directly; unmanaged implies shared.
+ unshareOneSlow();
+ }
+ }
+
+ // /**
+ // * Coalesce this MemBuf chain into a single buffer.
+ // *
+ // * This method moves all of the data in this MemBuf chain into a single
+ // * contiguous buffer, if it is not already in one buffer. After coalesce()
+ // * returns, this MemBuf will be a chain of length one. Other MemBufs in
+ // the
+ // * chain will be automatically deleted.
+ // *
+ // * After coalescing, the MemBuf will have at least as much headroom as the
+ // * first MemBuf in the chain, and at least as much tailroom as the last
+ // MemBuf
+ // * in the chain.
+ // *
+ // * Throws std::bad_alloc on error. On error the MemBuf chain will be
+ // * unmodified.
+ // *
+ // * Returns ByteRange that points to the data MemBuf stores.
+ // */
+ // ByteRange coalesce() {
+ // const std::size_t newHeadroom = headroom();
+ // const std::size_t newTailroom = prev()->tailroom();
+ // return coalesceWithHeadroomTailroom(newHeadroom, newTailroom);
+ // }
+
+ // /**
+ // * This is similar to the coalesce() method, except this allows to set a
+ // * headroom and tailroom after coalescing.
+ // *
+ // * Returns ByteRange that points to the data MemBuf stores.
+ // */
+ // ByteRange coalesceWithHeadroomTailroom(
+ // std::size_t newHeadroom,
+ // std::size_t newTailroom) {
+ // if (isChained()) {
+ // coalesceAndReallocate(
+ // newHeadroom, computeChainDataLength(), this, newTailroom);
+ // }
+ // return ByteRange(data_, length_);
+ // }
+
+ /**
+ * Ensure that this chain has at least maxLength bytes available as a
+ * contiguous memory range.
+ *
+ * This method coalesces whole buffers in the chain into this buffer as
+ * necessary until this buffer's length() is at least maxLength.
+ *
+ * After coalescing, the MemBuf will have at least as much headroom as the
+ * first MemBuf in the chain, and at least as much tailroom as the last MemBuf
+ * that was coalesced.
+ *
+ * Throws std::bad_alloc or std::overflow_error on error. On error the MemBuf
+ * chain will be unmodified. Throws std::overflow_error if maxLength is
+ * longer than the total chain length.
+ *
+ * Upon return, either enough of the chain was coalesced into a contiguous
+ * region, or the entire chain was coalesced. That is,
+ * length() >= maxLength || !isChained() is true.
+ */
+ void gather(std::size_t maxLength) {
+ if (!isChained() || length_ >= maxLength) {
+ return;
+ }
+ coalesceSlow(maxLength);
+ }
+
+ /**
+ * Return a new MemBuf chain sharing the same data as this chain.
+ *
+ * The new MemBuf chain will normally point to the same underlying data
+ * buffers as the original chain. (The one exception to this is if some of
+ * the MemBufs in this chain contain small internal data buffers which cannot
+ * be shared.)
+ */
+ std::unique_ptr<MemBuf> clone() const;
+
+ /**
+ * Similar to clone(). But returns MemBuf by value rather than heap-allocating
+ * it.
+ */
+ MemBuf cloneAsValue() const;
+
+ /**
+ * Return a new MemBuf with the same data as this MemBuf.
+ *
+ * The new MemBuf returned will not be part of a chain (even if this MemBuf is
+ * part of a larger chain).
+ */
+ std::unique_ptr<MemBuf> cloneOne() const;
+
+ /**
+ * Similar to cloneOne(). But returns MemBuf by value rather than
+ * heap-allocating it.
+ */
+ MemBuf cloneOneAsValue() const;
+
+ /**
+ * Return a new unchained MemBuf that may share the same data as this chain.
+ *
+ * If the MemBuf chain is not chained then the new MemBuf will point to the
+ * same underlying data buffer as the original chain. Otherwise, it will clone
+ * and coalesce the MemBuf chain.
+ *
+ * The new MemBuf will have at least as much headroom as the first MemBuf in
+ * the chain, and at least as much tailroom as the last MemBuf in the chain.
+ *
+ * Throws std::bad_alloc on error.
+ */
+ std::unique_ptr<MemBuf> cloneCoalesced() const;
+
+ /**
+ * This is similar to the cloneCoalesced() method, except this allows to set a
+ * headroom and tailroom for the new MemBuf.
+ */
+ std::unique_ptr<MemBuf> cloneCoalescedWithHeadroomTailroom(
+ std::size_t newHeadroom, std::size_t newTailroom) const;
+
+ /**
+ * Similar to cloneCoalesced(). But returns MemBuf by value rather than
+ * heap-allocating it.
+ */
+ MemBuf cloneCoalescedAsValue() const;
+
+ /**
+ * This is similar to the cloneCoalescedAsValue() method, except this allows
+ * to set a headroom and tailroom for the new MemBuf.
+ */
+ MemBuf cloneCoalescedAsValueWithHeadroomTailroom(
+ std::size_t newHeadroom, std::size_t newTailroom) const;
+
+ /**
+ * Similar to Clone(). But use other as the head node. Other nodes in the
+ * chain (if any) will be allocted on heap.
+ */
+ void cloneInto(MemBuf& other) const { other = cloneAsValue(); }
+
+ /**
+ * Similar to CloneOne(). But to fill an existing MemBuf instead of a new
+ * MemBuf.
+ */
+ void cloneOneInto(MemBuf& other) const { other = cloneOneAsValue(); }
+
+ /**
+ * Return an iovector suitable for e.g. writev()
+ *
+ * auto iov = buf->getIov();
+ * auto xfer = writev(fd, iov.data(), iov.size());
+ *
+ * Naturally, the returned iovector is invalid if you modify the buffer
+ * chain.
+ */
+ std::vector<struct iovec> getIov() const;
+
+ /**
+ * Update an existing iovec array with the MemBuf data.
+ *
+ * New iovecs will be appended to the existing vector; anything already
+ * present in the vector will be left unchanged.
+ *
+ * Naturally, the returned iovec data will be invalid if you modify the
+ * buffer chain.
+ */
+ void appendToIov(std::vector<struct iovec>* iov) const;
+
+ /**
+ * Fill an iovec array with the MemBuf data.
+ *
+ * Returns the number of iovec filled. If there are more buffer than
+ * iovec, returns 0. This version is suitable to use with stack iovec
+ * arrays.
+ *
+ * Naturally, the filled iovec data will be invalid if you modify the
+ * buffer chain.
+ */
+ size_t fillIov(struct iovec* iov, size_t len) const;
+
+ /**
+ * A helper that wraps a number of iovecs into an MemBuf chain. If count ==
+ * 0, then a zero length buf is returned. This function never returns
+ * nullptr.
+ */
+ static std::unique_ptr<MemBuf> wrapIov(const iovec* vec, size_t count);
+
+ /**
+ * A helper that takes ownerships a number of iovecs into an MemBuf chain. If
+ * count == 0, then a zero length buf is returned. This function never
+ * returns nullptr.
+ */
+ static std::unique_ptr<MemBuf> takeOwnershipIov(const iovec* vec,
+ size_t count,
+ FreeFunction freeFn = nullptr,
+ void* userData = nullptr,
+ bool freeOnError = true);
+
+ /*
+ * Overridden operator new and delete.
+ * These perform specialized memory management to help support
+ * createCombined(), which allocates MemBuf objects together with the buffer
+ * data.
+ */
+ void* operator new(size_t size);
+ void* operator new(size_t size, void* ptr);
+ void operator delete(void* ptr);
+ void operator delete(void* ptr, void* placement);
+
+ // /**
+ // * Iteration support: a chain of MemBufs may be iterated through using
+ // * STL-style iterators over const ByteRanges. Iterators are only
+ // invalidated
+ // * if the MemBuf that they currently point to is removed.
+ // */
+ // Iterator cbegin() const;
+ // Iterator cend() const;
+ // Iterator begin() const;
+ // Iterator end() const;
+
+ /**
+ * Allocate a new null buffer.
+ *
+ * This can be used to allocate an empty MemBuf on the stack. It will have no
+ * space allocated for it. This is generally useful only to later use move
+ * assignment to fill out the MemBuf.
+ */
+ MemBuf() noexcept;
+
+ /**
+ * Move constructor and assignment operator.
+ *
+ * In general, you should only ever move the head of an MemBuf chain.
+ * Internal nodes in an MemBuf chain are owned by the head of the chain, and
+ * should not be moved from. (Technically, nothing prevents you from moving
+ * a non-head node, but the moved-to node will replace the moved-from node in
+ * the chain. This has implications for ownership, since non-head nodes are
+ * owned by the chain head. You are then responsible for relinquishing
+ * ownership of the moved-to node, and manually deleting the moved-from
+ * node.)
+ *
+ * With the move assignment operator, the destination of the move should be
+ * the head of an MemBuf chain or a solitary MemBuf not part of a chain. If
+ * the move destination is part of a chain, all other MemBufs in the chain
+ * will be deleted.
+ */
+ MemBuf(MemBuf&& other) noexcept;
+ MemBuf& operator=(MemBuf&& other) noexcept;
+
+ MemBuf(const MemBuf& other);
+ MemBuf& operator=(const MemBuf& other);
+
+ private:
+ enum FlagsEnum : uintptr_t {
+ // Adding any more flags would not work on 32-bit architectures,
+ // as these flags are stashed in the least significant 2 bits of a
+ // max-align-aligned pointer.
+ flag_free_shared_info = 0x1,
+ flag_maybe_shared = 0x2,
+ flag_mask = flag_free_shared_info | flag_maybe_shared
+ };
+
+ struct SharedInfo {
+ SharedInfo();
+ SharedInfo(FreeFunction fn, void* arg);
+
+ // A pointer to a function to call to free the buffer when the refcount
+ // hits 0. If this is null, free() will be used instead.
+ FreeFunction freeFn;
+ void* userData;
+ std::atomic<uint32_t> refcount;
+ bool externallyShared{false};
+ };
+ // Helper structs for use by operator new and delete
+ struct HeapPrefix;
+ struct HeapStorage;
+ struct HeapFullStorage;
+
+ /**
+ * Create a new MemBuf pointing to an external buffer.
+ *
+ * The caller is responsible for holding a reference count for this new
+ * MemBuf. The MemBuf constructor does not automatically increment the
+ * reference count.
+ */
+ struct InternalConstructor {}; // avoid conflicts
+ MemBuf(InternalConstructor, uintptr_t flagsAndSharedInfo, uint8_t* buf,
+ std::size_t capacity, uint8_t* data, std::size_t length) noexcept;
+
+ void unshareOneSlow();
+ void unshareChained();
+ void makeManagedChained();
+ void coalesceSlow();
+ void coalesceSlow(size_t maxLength);
+ // newLength must be the entire length of the buffers between this and
+ // end (no truncation)
+ void coalesceAndReallocate(size_t newHeadroom, size_t newLength, MemBuf* end,
+ size_t newTailroom);
+ void coalesceAndReallocate(size_t newLength, MemBuf* end) {
+ coalesceAndReallocate(headroom(), newLength, end, end->prev_->tailroom());
+ }
+ void decrementRefcount();
+ void reserveSlow(std::size_t minHeadroom, std::size_t minTailroom);
+ void freeExtBuffer();
+
+ static size_t goodExtBufferSize(std::size_t minCapacity);
+ static void initExtBuffer(uint8_t* buf, size_t mallocSize,
+ SharedInfo** infoReturn,
+ std::size_t* capacityReturn);
+ static void allocExtBuffer(std::size_t minCapacity, uint8_t** bufReturn,
+ SharedInfo** infoReturn,
+ std::size_t* capacityReturn);
+ static void releaseStorage(HeapStorage* storage, uint16_t freeFlags);
+ static void freeInternalBuf(void* buf, void* userData);
+
+ /*
+ * Member variables
+ */
+
+ /*
+ * Links to the next and the previous MemBuf in this chain.
+ *
+ * The chain is circularly linked (the last element in the chain points back
+ * at the head), and next_ and prev_ can never be null. If this MemBuf is the
+ * only element in the chain, next_ and prev_ will both point to this.
+ */
+ MemBuf* next_{this};
+ MemBuf* prev_{this};
+
+ /*
+ * A pointer to the start of the data referenced by this MemBuf, and the
+ * length of the data.
+ *
+ * This may refer to any subsection of the actual buffer capacity.
+ */
+ uint8_t* data_{nullptr};
+ uint8_t* buf_{nullptr};
+ std::size_t length_{0};
+ std::size_t capacity_{0};
+
+ // Pack flags in least significant 2 bits, sharedInfo in the rest
+ mutable uintptr_t flags_and_shared_info_{0};
+
+ static inline uintptr_t packFlagsAndSharedInfo(uintptr_t flags,
+ SharedInfo* info) {
+ uintptr_t uinfo = reinterpret_cast<uintptr_t>(info);
+ return flags | uinfo;
+ }
+
+ inline SharedInfo* sharedInfo() const {
+ return reinterpret_cast<SharedInfo*>(flags_and_shared_info_ & ~flag_mask);
+ }
+
+ inline void setSharedInfo(SharedInfo* info) {
+ uintptr_t uinfo = reinterpret_cast<uintptr_t>(info);
+ flags_and_shared_info_ = (flags_and_shared_info_ & flag_mask) | uinfo;
+ }
+
+ inline uintptr_t flags() const { return flags_and_shared_info_ & flag_mask; }
+
+ // flags_ are changed from const methods
+ inline void setFlags(uintptr_t flags) const {
+ flags_and_shared_info_ |= flags;
+ }
+
+ inline void clearFlags(uintptr_t flags) const {
+ flags_and_shared_info_ &= ~flags;
+ }
+
+ inline void setFlagsAndSharedInfo(uintptr_t flags, SharedInfo* info) {
+ flags_and_shared_info_ = packFlagsAndSharedInfo(flags, info);
+ }
+
+ struct DeleterBase {
+ virtual ~DeleterBase() {}
+ virtual void dispose(void* p) = 0;
+ };
+
+ template <class UniquePtr>
+ struct UniquePtrDeleter : public DeleterBase {
+ typedef typename UniquePtr::pointer Pointer;
+ typedef typename UniquePtr::deleter_type Deleter;
+
+ explicit UniquePtrDeleter(Deleter deleter) : deleter_(std::move(deleter)) {}
+ void dispose(void* p) override {
+ try {
+ deleter_(static_cast<Pointer>(p));
+ delete this;
+ } catch (...) {
+ abort();
+ }
+ }
+
+ private:
+ Deleter deleter_;
+ };
+
+ static void freeUniquePtrBuffer(void* ptr, void* userData) {
+ static_cast<DeleterBase*>(userData)->dispose(ptr);
+ }
+};
+
+// template <class UniquePtr>
+// typename std::enable_if<
+// detail::IsUniquePtrToSL<UniquePtr>::value,
+// std::unique_ptr<MemBuf>>::type
+// MemBuf::takeOwnership(UniquePtr&& buf, size_t count) {
+// size_t size = count * sizeof(typename UniquePtr::element_type);
+// auto deleter = new UniquePtrDeleter<UniquePtr>(buf.get_deleter());
+// return takeOwnership(
+// buf.release(), size, &MemBuf::freeUniquePtrBuffer, deleter);
+// }
+
+inline std::unique_ptr<MemBuf> MemBuf::copyBuffer(const void* data,
+ std::size_t size,
+ std::size_t headroom,
+ std::size_t minTailroom) {
+ std::size_t capacity = headroom + size + minTailroom;
+ std::unique_ptr<MemBuf> buf = MemBuf::create(capacity);
+ buf->advance(headroom);
+ if (size != 0) {
+ memcpy(buf->writableData(), data, size);
+ }
+ buf->append(size);
+ return buf;
+}
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/min_filter.h b/libtransport/src/hicn/transport/utils/min_filter.h
new file mode 100755
index 000000000..acb081edc
--- /dev/null
+++ b/libtransport/src/hicn/transport/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/hicn/transport/utils/object_pool.h b/libtransport/src/hicn/transport/utils/object_pool.h
new file mode 100755
index 000000000..d4d8e18d6
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/object_pool.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
+
+// TODO
+#include <hicn/transport/utils/spinlock.h>
+
+#include <deque>
+#include <memory>
+#include <mutex>
+
+namespace utils {
+
+template <typename T>
+class ObjectPool {
+ class ObjectDeleter {
+ public:
+ ObjectDeleter(ObjectPool<T> *pool = nullptr) : pool_(pool) {}
+
+ void operator()(T *t) {
+ if (pool_) {
+ pool_->add(t);
+ } else {
+ delete t;
+ }
+ }
+
+ private:
+ ObjectPool<T> *pool_;
+ };
+
+ public:
+ using Ptr = std::unique_ptr<T, ObjectDeleter>;
+
+ ObjectPool() {}
+
+ std::pair<bool, Ptr> get() {
+ if (object_pool_.empty()) {
+ return std::make_pair<bool, Ptr>(false, makePtr(nullptr));
+ }
+
+ utils::SpinLock::Acquire locked(object_pool_lock_);
+ auto ret = std::move(object_pool_.front());
+ object_pool_.pop_front();
+ return std::make_pair<bool, Ptr>(true, std::move(ret));
+ }
+
+ void add(T *object) {
+ utils::SpinLock::Acquire locked(object_pool_lock_);
+ object_pool_.emplace_back(makePtr(object));
+ }
+
+ Ptr makePtr(T *object) { return Ptr(object, ObjectDeleter(this)); }
+
+ private:
+ // No copies
+ ObjectPool(const ObjectPool &other) = delete;
+
+ utils::SpinLock object_pool_lock_;
+ std::deque<Ptr> object_pool_;
+};
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/ring_buffer.h b/libtransport/src/hicn/transport/utils/ring_buffer.h
new file mode 100755
index 000000000..52bcd81c4
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/ring_buffer.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 <atomic>
+#include <cstddef>
+
+namespace utils {
+
+/**
+ * NOTE: Single consumer single producer ring buffer
+ */
+template <typename Element, std::size_t Size>
+class CircularFifo {
+ public:
+ enum { Capacity = Size + 1 };
+
+ CircularFifo() : tail_(0), head_(0), size_(0) {}
+ virtual ~CircularFifo() {}
+
+ bool push(const Element& item);
+ bool push(Element&& item);
+ bool pop(Element& item);
+
+ bool wasEmpty() const;
+ bool wasFull() const;
+ bool isLockFree() const;
+ std::size_t size() const;
+
+ private:
+ std::size_t increment(std::size_t idx) const;
+ std::atomic<std::size_t> tail_; // tail(input) index
+ Element array_[Capacity];
+ std::atomic<std::size_t> head_; // head(output) index
+ std::atomic<std::size_t> size_;
+};
+
+template <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::push(const Element& item) {
+ const auto current_tail = tail_.load(std::memory_order_relaxed);
+ const auto next_tail = increment(current_tail);
+ if (next_tail != head_.load(std::memory_order_acquire)) {
+ array_[current_tail] = item;
+ tail_.store(next_tail, std::memory_order_release);
+ size_++;
+ return true;
+ }
+
+ // full queue
+ return false;
+}
+
+/**
+ * Push by move
+ */
+template <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::push(Element&& item) {
+ const auto current_tail = tail_.load(std::memory_order_relaxed);
+ const auto next_tail = increment(current_tail);
+ if (next_tail != head_.load(std::memory_order_acquire)) {
+ array_[current_tail] = std::move(item);
+ tail_.store(next_tail, std::memory_order_release);
+ size_++;
+ return true;
+ }
+
+ // full queue
+ return false;
+}
+
+// Pop by Consumer can only update the head
+// (load with relaxed, store with release)
+// the tail must be accessed with at least acquire
+template <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::pop(Element& item) {
+ const auto current_head = head_.load(std::memory_order_relaxed);
+ if (current_head == tail_.load(std::memory_order_acquire)) {
+ return false; // empty queue
+ }
+
+ item = std::move(array_[current_head]);
+ head_.store(increment(current_head), std::memory_order_release);
+ size_--;
+ return true;
+}
+
+template <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::wasEmpty() const {
+ // snapshot with acceptance of that this comparison operation is not atomic
+ return (head_.load() == tail_.load());
+}
+
+// snapshot with acceptance that this comparison is not atomic
+template <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::wasFull() const {
+ const auto next_tail =
+ increment(tail_.load()); // acquire, we dont know who call
+ return (next_tail == head_.load());
+}
+
+template <typename Element, std::size_t Size>
+bool CircularFifo<Element, Size>::isLockFree() const {
+ return (tail_.is_lock_free() && head_.is_lock_free());
+}
+
+template <typename Element, std::size_t Size>
+std::size_t CircularFifo<Element, Size>::increment(std::size_t idx) const {
+ return (idx + 1) % Capacity;
+}
+
+template <typename Element, std::size_t Size>
+std::size_t CircularFifo<Element, Size>::size() const {
+ return size_.load(std::memory_order_relaxed);
+}
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/sharable_vector.h b/libtransport/src/hicn/transport/utils/sharable_vector.h
new file mode 100755
index 000000000..31adff1ad
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/sharable_vector.h
@@ -0,0 +1,30 @@
+/*
+ * 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 <memory>
+#include <vector>
+
+namespace utils {
+
+template <class T>
+class SharableVector : public std::vector<T>,
+ public std::enable_shared_from_this<SharableVector<T>> {
+ public:
+ virtual ~SharableVector(){};
+};
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/signer.cc b/libtransport/src/hicn/transport/utils/signer.cc
new file mode 100755
index 000000000..c11d5e183
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/signer.cc
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hicn/transport/errors/malformed_ahpacket_exception.h>
+#include <hicn/transport/utils/endianess.h>
+#include <hicn/transport/utils/membuf.h>
+#include <hicn/transport/utils/signer.h>
+#include <hicn/transport/utils/key_id.h>
+
+
+extern "C" {
+TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat")
+#include <hicn/hicn.h>
+#include <parc/security/parc_PublicKeySigner.h>
+#include <parc/security/parc_Security.h>
+#include <parc/security/parc_SymmetricKeySigner.h>
+}
+
+#include <chrono>
+
+#define ALLOW_UNALIGNED_READS 1
+
+namespace utils {
+
+uint8_t Signer::zeros[200] = {0};
+
+/*One signer_ per Private Key*/
+Signer::Signer(PARCKeyStore *keyStore, PARCCryptoSuite suite) {
+ switch (suite) {
+ case PARCCryptoSuite_NULL_CRC32C:
+ break;
+ case PARCCryptoSuite_ECDSA_SHA256:
+ case PARCCryptoSuite_RSA_SHA256:
+ case PARCCryptoSuite_DSA_SHA256:
+ case PARCCryptoSuite_RSA_SHA512:
+ this->signer_ =
+ parcSigner_Create(parcPublicKeySigner_Create(keyStore, suite),
+ PARCPublicKeySignerAsSigner);
+ this->key_id_ = parcSigner_CreateKeyId(this->signer_);
+ break;
+
+ case PARCCryptoSuite_HMAC_SHA512:
+ case PARCCryptoSuite_HMAC_SHA256:
+ default:
+ this->signer_ = parcSigner_Create(
+ parcSymmetricKeySigner_Create((PARCSymmetricKeyStore *)keyStore,
+ parcCryptoSuite_GetCryptoHash(suite)),
+ PARCSymmetricKeySignerAsSigner);
+ this->key_id_ = parcSigner_CreateKeyId(this->signer_);
+ break;
+ }
+}
+
+Signer::Signer(const PARCSigner *signer)
+ : signer_(parcSigner_Acquire(signer)),
+ key_id_(parcSigner_CreateKeyId(this->signer_)) {}
+
+Signer::~Signer() {
+ parcSigner_Release(&signer_);
+ parcKeyId_Release(&key_id_);
+}
+
+void Signer::sign(Packet &packet) {
+ // header chain points to the IP + TCP hicn header
+ utils::MemBuf *header_chain = packet.header_head_;
+ utils::MemBuf * payload_chain = packet.payload_head_;
+ uint8_t *hicn_packet = header_chain->writableData();
+ Packet::Format format = packet.getFormat();
+ std::size_t sign_len_bytes = parcSigner_GetSignatureSize(signer_);
+
+ if (!(format & HFO_AH)) {
+ throw errors::MalformedAHPacketException();
+ }
+
+ // Copy IP+TCP/ICMP header before zeroing them
+ hicn_header_t header_copy;
+ if (format & HFO_INET) {
+ memcpy(&header_copy, hicn_packet, sizeof(hicn_v4_hdr_t));
+ } else if (format & HFO_INET6) {
+ memcpy(&header_copy, hicn_packet, sizeof(hicn_v6_hdr_t));
+ }
+
+ std::size_t header_len = Packet::getHeaderSizeFromFormat(format);
+
+ packet.resetForHash();
+ packet.setSignatureSize(sign_len_bytes);
+
+ /* Fill the hicn_ah header */
+ using namespace std::chrono;
+ auto now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+ packet.setSignatureTimestamp(now);
+ // *reinterpret_cast<uint64_t*>(ah->signTime) = utils::hton<uint64_t>(now);
+ // // std::memcpy(&ah->hicn_ah.signTime, &sign_time, sizeof(ah->hicn_ah.signTime));
+
+ packet.setValidationAlgorithm(CryptoSuite(parcSigner_GetCryptoSuite(this->signer_)));
+ // ah->validationAlgorithm = parcSigner_GetCryptoSuite(this->signer_);
+
+ KeyId key_id;
+ key_id.first = (uint8_t *)parcBuffer_Overlay((PARCBuffer *) parcKeyId_GetKeyId(this->key_id_), 0);
+ packet.setKeyId(key_id);
+
+ // memcpy(ah->keyId,
+ // parcBuffer_Overlay((PARCBuffer *) parcKeyId_GetKeyId(this->key_id_), 0),
+ // sizeof(_ah_header_t::keyId));
+
+ // Calculate hash
+ utils::CryptoHasher hasher(parcSigner_GetCryptoHasher(signer_));
+ hasher.init();
+ hasher.updateBytes(hicn_packet, header_len);
+ hasher.updateBytes(zeros, sign_len_bytes);
+
+ for (utils::MemBuf *current = payload_chain; current != header_chain; current = current->next()) {
+ hasher.updateBytes(current->data(), current->length());
+ }
+
+ utils::CryptoHash hash = hasher.finalize();
+
+ PARCSignature *signature = parcSigner_SignDigest(this->signer_, hash.hash_);
+ PARCBuffer *buffer = parcSignature_GetSignature(signature);
+
+ PARCByteArray * byte_array = parcBuffer_Array(buffer);
+ uint8_t * bytes = parcByteArray_Array(byte_array);
+ size_t bytes_len = parcBuffer_Remaining(buffer);
+
+ if (bytes_len > sign_len_bytes) {
+ throw errors::MalformedAHPacketException();
+ }
+
+ /* Restore the resetted fields */
+ if (format & HFO_INET) {
+ memcpy(hicn_packet, &header_copy, sizeof(hicn_v4_hdr_t));
+ } else if (format & HFO_INET6) {
+ memcpy(hicn_packet, &header_copy, sizeof(hicn_v6_hdr_t));
+ }
+
+ int offset = sign_len_bytes - bytes_len;
+
+ std::unique_ptr<utils::MemBuf> signature_buffer;
+ std::unique_ptr<utils::MemBuf> tmp_buf = utils::MemBuf::takeOwnership(
+ bytes,
+ bytes_len,
+ bytes_len,
+ [](void* buf, void* userData){ parcSignature_Release((PARCSignature **)&userData); },
+ signature,
+ true);
+
+ if (offset) {
+ signature_buffer = utils::MemBuf::create(offset);
+ memset(signature_buffer->writableData(), 0, offset);
+ signature_buffer->append(offset);
+ signature_buffer->appendChain(std::move(tmp_buf));
+ } else {
+ signature_buffer = std::move(tmp_buf);
+ }
+
+ packet.setSignature(std::move(signature_buffer));
+}
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/signer.h b/libtransport/src/hicn/transport/utils/signer.h
new file mode 100755
index 000000000..7b54b63c8
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/signer.h
@@ -0,0 +1,69 @@
+/*
+ * 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/core/packet.h>
+
+extern "C" {
+#include <parc/security/parc_CryptoHashType.h>
+#include <parc/security/parc_CryptoSuite.h>
+#include <parc/security/parc_KeyStore.h>
+#include <parc/security/parc_Signer.h>
+}
+
+namespace utils {
+
+using Packet = transport::core::Packet;
+
+/**
+ * A signer can use a single key (asymmetric or symmetric) to sign a packet.
+ */
+class Signer {
+ friend class Identity;
+
+ public:
+ /**
+ * Create a Signer
+ *
+ * @param keyStore A keystore containing a private key or simmetric key to
+ * use to sign packet with this Signer.
+ * @param suite CryptoSuite to use to verify the signature
+ */
+ Signer(PARCKeyStore *keyStore, PARCCryptoSuite suite);
+
+ Signer(const PARCSigner *signer);
+
+ ~Signer();
+
+ /**
+ * @brief Sign a packet
+ *
+ * This method is general and must be used for Public-private key signature,
+ * HMAC and CRC.
+ *
+ * @param packet A pointer to the header of the packet to sign. Mutable
+ * field in the packet must be set to 0.
+ * @param key_id Indentifier of the key to use to generate the signature.
+ */
+ void sign(Packet &packet);
+
+ private:
+ PARCSigner *signer_;
+ PARCKeyId *key_id_;
+ static uint8_t zeros[200];
+};
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/socket.h b/libtransport/src/hicn/transport/utils/socket.h
new file mode 100755
index 000000000..ab8578f91
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/socket.h
@@ -0,0 +1,267 @@
+/*
+ * 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/config.h>
+#include <hicn/transport/core/content_object.h>
+#include <hicn/transport/core/facade.h>
+#include <hicn/transport/core/interest.h>
+#include <hicn/transport/core/manifest_format_fixed.h>
+#include <hicn/transport/core/manifest_inline.h>
+#include <hicn/transport/core/name.h>
+#include <hicn/transport/transport/download_observer.h>
+#include <hicn/transport/transport/socket_options_default_values.h>
+#include <hicn/transport/transport/socket_options_keys.h>
+#include <hicn/transport/utils/crypto_suite.h>
+#include <hicn/transport/utils/identity.h>
+#include <hicn/transport/utils/verifier.h>
+
+#define SOCKET_OPTION_GET 0
+#define SOCKET_OPTION_NOT_GET 1
+#define SOCKET_OPTION_SET 2
+#define SOCKET_OPTION_NOT_SET 3
+#define SOCKET_OPTION_DEFAULT 12345
+
+#define VOID_HANDLER 0
+
+namespace transport {
+
+namespace transport {
+
+template <typename PortalType>
+class Socket;
+class ConsumerSocket;
+class ProducerSocket;
+
+using Interest = core::Interest;
+using ContentObject = core::ContentObject;
+using Name = core::Name;
+using ContentObjectManifest = core::ManifestInline<ContentObject, core::Fixed>;
+using InterestManifest = core::ManifestInline<Interest, core::Fixed>;
+using HashAlgorithm = core::HashAlgorithm;
+using CryptoSuite = utils::CryptoSuite;
+using Identity = utils::Identity;
+using Verifier = utils::Verifier;
+
+using HicnForwarderPortal = core::HicnForwarderPortal;
+
+#ifdef __linux__
+#ifndef __ANDROID__
+using RawSocketPortal = core::RawSocketPortal;
+#endif
+#endif
+
+#ifdef __vpp__
+using VPPForwarderPortal = core::VPPForwarderPortal;
+using BaseSocket = Socket<VPPForwarderPortal>;
+using BasePortal = VPPForwarderPortal;
+#else
+using BaseSocket = Socket<HicnForwarderPortal>;
+using BasePortal = HicnForwarderPortal;
+#endif
+
+using PayloadType = core::PayloadType;
+using Prefix = core::Prefix;
+using Array = utils::Array<uint8_t>;
+
+using ConsumerInterestCallback =
+ std::function<void(ConsumerSocket &, const Interest &)>;
+
+using ConsumerContentCallback =
+ std::function<void(ConsumerSocket &, std::size_t, const std::error_code &)>;
+
+using ConsumerTimerCallback =
+ std::function<void(ConsumerSocket &, std::size_t,
+ std::chrono::milliseconds &, float, uint32_t, uint32_t)>;
+
+using ProducerContentCallback = std::function<void(
+ ProducerSocket &, const std::error_code &, uint64_t bytes_written)>;
+
+using ConsumerContentObjectCallback =
+ std::function<void(ConsumerSocket &, const ContentObject &)>;
+
+using ConsumerContentObjectVerificationCallback =
+ std::function<bool(ConsumerSocket &, const ContentObject &)>;
+
+using ConsumerManifestCallback =
+ std::function<void(ConsumerSocket &, const ContentObjectManifest &)>;
+
+using ProducerContentObjectCallback =
+ std::function<void(ProducerSocket &, ContentObject &)>;
+
+using ProducerInterestCallback =
+ std::function<void(ProducerSocket &, const Interest &)>;
+
+using ProducerInterestCallback =
+ std::function<void(ProducerSocket &, const Interest &)>;
+
+template <typename PortalType>
+class Socket {
+ static_assert(std::is_same<PortalType, HicnForwarderPortal>::value
+#ifdef __linux__
+#ifndef __ANDROID__
+ || std::is_same<PortalType, RawSocketPortal>::value
+#ifdef __vpp__
+ || std::is_same<PortalType, VPPForwarderPortal>::value
+#endif
+#endif
+ ,
+#else
+ ,
+
+#endif
+ "This class is not allowed as Portal");
+
+ public:
+ typedef PortalType Portal;
+
+ virtual asio::io_service &getIoService() = 0;
+
+ virtual void connect() = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ uint32_t socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ double socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ bool socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ Name socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ std::list<Prefix> socket_option_value) = 0;
+
+ virtual int setSocketOption(
+ int socket_option_key,
+ ProducerContentObjectCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ ProducerInterestCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ ProducerContentCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(
+ int socket_option_key,
+ ConsumerContentObjectVerificationCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(
+ int socket_option_key,
+ ConsumerContentObjectCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ ConsumerInterestCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ ConsumerContentCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ ConsumerManifestCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ IcnObserver *socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ HashAlgorithm socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ CryptoSuite socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ const Identity &socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ ConsumerTimerCallback socket_option_value) = 0;
+
+ virtual int setSocketOption(int socket_option_key,
+ const std::string &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ uint32_t &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ double &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ bool &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ Name &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ std::list<Prefix> &socket_option_value) = 0;
+
+ virtual int getSocketOption(
+ int socket_option_key,
+ ProducerContentObjectCallback &socket_option_value) = 0;
+
+ virtual int getSocketOption(
+ int socket_option_key, ProducerInterestCallback &socket_option_value) = 0;
+
+ virtual int getSocketOption(
+ int socket_option_key,
+ ConsumerContentObjectVerificationCallback &socket_option_value) = 0;
+
+ virtual int getSocketOption(
+ int socket_option_key,
+ ConsumerContentObjectCallback &socket_option_value) = 0;
+
+ virtual int getSocketOption(
+ int socket_option_key, ConsumerInterestCallback &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ ConsumerContentCallback &socket_option_value) = 0;
+
+ virtual int getSocketOption(
+ int socket_option_key, ConsumerManifestCallback &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ ProducerContentCallback &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ std::shared_ptr<Portal> &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ IcnObserver **socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ HashAlgorithm &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ CryptoSuite &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ Identity &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ std::string &socket_option_value) = 0;
+
+ virtual int getSocketOption(int socket_option_key,
+ ConsumerTimerCallback &socket_option_value) = 0;
+
+ protected:
+ virtual ~Socket(){};
+
+ protected:
+ std::string output_interface_;
+};
+
+} // namespace transport
+
+} // namespace transport
diff --git a/libtransport/src/hicn/transport/utils/spinlock.h b/libtransport/src/hicn/transport/utils/spinlock.h
new file mode 100755
index 000000000..33e5cda85
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/spinlock.h
@@ -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.
+ */
+
+#pragma once
+
+#include <atomic>
+
+namespace utils {
+
+class SpinLock : private std::atomic_flag {
+ public:
+ class Acquire {
+ public:
+ Acquire(SpinLock& spin_lock) : spin_lock_(spin_lock) { spin_lock_.lock(); }
+
+ ~Acquire() { spin_lock_.unlock(); }
+
+ // No copies
+ Acquire& operator=(const Acquire&) = delete;
+ Acquire(const Acquire&) = delete;
+
+ private:
+ SpinLock& spin_lock_;
+ };
+
+ SpinLock() : std::atomic_flag(false) {}
+
+ void lock() {
+ // busy-wait
+ while (std::atomic_flag::test_and_set(std::memory_order_acquire))
+ ;
+ }
+
+ void unlock() { clear(std::memory_order_release); }
+
+ bool tryLock() {
+ return std::atomic_flag::test_and_set(std::memory_order_acquire);
+ }
+};
+
+} // namespace utils \ No newline at end of file
diff --git a/libtransport/src/hicn/transport/utils/stream_buffer.h b/libtransport/src/hicn/transport/utils/stream_buffer.h
new file mode 100755
index 000000000..adfb696f2
--- /dev/null
+++ b/libtransport/src/hicn/transport/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/hicn/transport/utils/string_tokenizer.cc b/libtransport/src/hicn/transport/utils/string_tokenizer.cc
new file mode 100755
index 000000000..9d1911080
--- /dev/null
+++ b/libtransport/src/hicn/transport/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 = 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/hicn/transport/utils/string_tokenizer.h b/libtransport/src/hicn/transport/utils/string_tokenizer.h
new file mode 100755
index 000000000..36630eb58
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/string_tokenizer.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <string>
+
+namespace utils {
+
+class StringTokenizer {
+ public:
+ StringTokenizer(const std::string &str);
+ StringTokenizer(const std::string &str, const std::string &delim);
+
+ bool hasMoreTokens();
+ std::string nextToken();
+
+ private:
+ std::string str_;
+ std::string delimiter_;
+};
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/test.h b/libtransport/src/hicn/transport/utils/test.h
new file mode 100755
index 000000000..e3dd619ac
--- /dev/null
+++ b/libtransport/src/hicn/transport/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/hicn/transport/utils/uri.cc b/libtransport/src/hicn/transport/utils/uri.cc
new file mode 100755
index 000000000..33eb8b45b
--- /dev/null
+++ b/libtransport/src/hicn/transport/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
diff --git a/libtransport/src/hicn/transport/utils/uri.h b/libtransport/src/hicn/transport/utils/uri.h
new file mode 100755
index 000000000..7c28e8552
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/uri.h
@@ -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.
+ */
+
+#pragma once
+
+#include <algorithm> // find
+#include <string>
+
+namespace utils {
+
+class Uri {
+ typedef std::string::const_iterator iterator_t;
+
+ public:
+ Uri();
+
+ Uri &parse(const std::string &uri);
+
+ Uri &parseProtocolAndLocator(const std::string &locator);
+
+ std::string getQueryString();
+
+ std::string getPath();
+
+ std::string getProtocol();
+
+ std::string getLocator();
+
+ std::string getPort();
+
+ private:
+ std::string query_string_, path_, protocol_, locator_, port_;
+}; // uri
+
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/verifier.cc b/libtransport/src/hicn/transport/utils/verifier.cc
new file mode 100755
index 000000000..9a3de43c1
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/verifier.cc
@@ -0,0 +1,193 @@
+/*
+ * 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/packet.h>
+#include <hicn/transport/errors/malformed_ahpacket_exception.h>
+#include <hicn/transport/portability/portability.h>
+#include <hicn/transport/utils/key_id.h>
+#include <hicn/transport/utils/log.h>
+#include <hicn/transport/utils/verifier.h>
+
+extern "C" {
+TRANSPORT_CLANG_DISABLE_WARNING("-Wextern-c-compat")
+#include <hicn/hicn.h>
+#include <parc/security/parc_CertificateFactory.h>
+#include <parc/security/parc_InMemoryVerifier.h>
+#include <parc/security/parc_Security.h>
+}
+
+#include <sys/stat.h>
+
+namespace utils {
+
+TRANSPORT_ALWAYS_INLINE bool file_exists(const std::string &name) {
+ struct stat buffer;
+ return (stat(name.c_str(), &buffer) == 0);
+}
+
+uint8_t Verifier::zeros[200] = {0};
+
+Verifier::Verifier() {
+ parcSecurity_Init();
+ PARCInMemoryVerifier *in_memory_verifier = parcInMemoryVerifier_Create();
+ this->verifier_ =
+ parcVerifier_Create(in_memory_verifier, PARCInMemoryVerifierAsVerifier);
+ parcInMemoryVerifier_Release(&in_memory_verifier);
+}
+
+Verifier::~Verifier() {
+ parcVerifier_Release(&verifier_);
+ parcSecurity_Fini();
+}
+
+/*
+ * TODO: Unsupported in libparc
+ */
+bool Verifier::hasKey(PARCKeyId *keyId) { return false; }
+
+/*
+ * TODO: signal errors without trap.
+ */
+bool Verifier::addKey(PARCKey *key) {
+ parcVerifier_AddKey(this->verifier_, key);
+ return true;
+}
+
+PARCKeyId * Verifier::addKeyFromCertificate(const std::string &file_name) {
+ PARCCertificateFactory *factory = parcCertificateFactory_Create(PARCCertificateType_X509,
+ PARCContainerEncoding_PEM);
+ parcAssertNotNull(factory, "Expected non-NULL factory");
+
+ if (!file_exists(file_name)) {
+ TRANSPORT_LOGW("Warning! The certificate %s file does not exist",
+ file_name.c_str());
+ return nullptr;
+ }
+
+ PARCCertificate *certificate =
+ parcCertificateFactory_CreateCertificateFromFile(
+ factory, (char *)file_name.c_str(), NULL);
+
+ PARCKey *key = parcCertificate_GetPublicKey(certificate);
+ addKey(key);
+
+ PARCKeyId *ret = parcKeyId_Acquire(parcKey_GetKeyId(key));
+
+ // parcKey_Release(&key);
+ // parcCertificate_Release(&certificate);
+ // parcCertificateFactory_Release(&factory);
+
+ return ret;
+}
+
+int Verifier::verify(const Packet &packet) {
+ bool valid = false;
+
+ // header chain points to the IP + TCP hicn header
+ utils::MemBuf *header_chain = packet.header_head_;
+ utils::MemBuf *payload_chain = packet.payload_head_;
+ uint8_t *hicn_packet = header_chain->writableData();
+ Packet::Format format = packet.getFormat();
+
+ if (!(packet.format_ & HFO_AH)) {
+ throw errors::MalformedAHPacketException();
+ }
+
+ // Copy IP+TCP/ICMP header before zeroing them
+ hicn_header_t header_copy;
+ if (format & HFO_INET) {
+ memcpy(&header_copy, hicn_packet, sizeof(hicn_v4_hdr_t));
+ } else if (format & HFO_INET6) {
+ memcpy(&header_copy, hicn_packet, sizeof(hicn_v6_hdr_t));
+ }
+
+ std::size_t header_len = Packet::getHeaderSizeFromFormat(format);
+
+ PARCCryptoSuite suite =
+ static_cast<PARCCryptoSuite>(packet.getValidationAlgorithm());
+ KeyId _key_id = packet.getKeyId();
+ PARCBuffer *buffer =
+ parcBuffer_Wrap(_key_id.first, _key_id.second, 0, _key_id.second);
+ PARCKeyId *key_id = parcKeyId_Create(buffer);
+ parcBuffer_Release(&buffer);
+
+ int ah_payload_len = header_chain->next()->length();
+ uint8_t *signature = header_chain->next()->writableData();
+
+ // Reset fields that should not appear in the signature
+ const_cast<Packet &>(packet).resetForHash();
+
+ PARCCryptoHashType hashtype = parcCryptoSuite_GetCryptoHash(suite);
+ utils::CryptoHasher hasher(
+ parcVerifier_GetCryptoHasher(verifier_, key_id, hashtype));
+
+ hasher.init()
+ .updateBytes(hicn_packet, header_len)
+ .updateBytes(zeros, ah_payload_len);
+
+ for (utils::MemBuf *current = payload_chain; current != header_chain;
+ current = current->next()) {
+ hasher.updateBytes(current->data(), current->length());
+ }
+
+ utils::CryptoHash hash = hasher.finalize();
+ PARCCryptoHash *hash_computed_locally = hash.hash_;
+
+ PARCBuffer *bits =
+ parcBuffer_Wrap(signature, ah_payload_len, 0, ah_payload_len);
+ parcBuffer_Rewind(bits);
+
+ /* IF the signature algo is ECDSA, the signature might be shorter than the
+ * signature field */
+ PARCSigningAlgorithm algo = parcCryptoSuite_GetSigningAlgorithm(suite);
+ while (algo == PARCSigningAlgorithm_ECDSA && parcBuffer_HasRemaining(bits) &&
+ parcBuffer_GetUint8(bits) == 0)
+ ;
+
+ if (algo == PARCSigningAlgorithm_ECDSA) {
+ parcBuffer_SetPosition(bits, parcBuffer_Position(bits) - 1);
+ }
+
+ if (!parcBuffer_HasRemaining(bits)) {
+ parcKeyId_Release(&key_id);
+ parcBuffer_Release(&bits);
+ return valid;
+ }
+
+ PARCSignature *signatureToVerify = parcSignature_Create(
+ parcCryptoSuite_GetSigningAlgorithm(suite), hashtype, bits);
+
+ if (algo == PARCSigningAlgorithm_RSA) {
+ parcBuffer_SetPosition(bits, 0);
+ }
+
+ valid = parcVerifier_VerifyDigestSignature(
+ verifier_, key_id, hash_computed_locally, suite, signatureToVerify);
+
+ /* Restore the resetted fields */
+ if (format & HFO_INET) {
+ memcpy(hicn_packet, &header_copy, sizeof(hicn_v4_hdr_t));
+ } else if (format & HFO_INET6) {
+ memcpy(hicn_packet, &header_copy, sizeof(hicn_v6_hdr_t));
+ }
+
+ parcKeyId_Release(&key_id);
+
+ parcBuffer_Release(&bits);
+ parcSignature_Release(&signatureToVerify);
+
+ return valid;
+}
+} // namespace utils
diff --git a/libtransport/src/hicn/transport/utils/verifier.h b/libtransport/src/hicn/transport/utils/verifier.h
new file mode 100755
index 000000000..6313a7240
--- /dev/null
+++ b/libtransport/src/hicn/transport/utils/verifier.h
@@ -0,0 +1,85 @@
+/*
+ * 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/core/packet.h>
+
+extern "C" {
+#include <parc/security/parc_KeyId.h>
+#include <parc/security/parc_Verifier.h>
+}
+
+namespace utils {
+
+using Packet = transport::core::Packet;
+
+/**
+ * A verifier holds a crypto cache that contains all the keys to use for
+ * verify signatures/hmacs.
+ */
+class Verifier {
+ public:
+ Verifier();
+
+ ~Verifier();
+
+ /**
+ * @brief Check if a key is already in this Verifier.
+ *
+ * A PARCVerifier contains a CryptoCache with a set of key to use for
+ * verification purposes.
+ *
+ * @param keyId Identifier of the key to match in the CryptoCache of the
+ * Verifier.
+ * @return true if the key is found, false otherwise.
+ */
+ bool hasKey(PARCKeyId *keyId);
+
+ /**
+ * @brief Add a key to this Verifier
+ *
+ * @param key to add
+ * @return true if the key was added successfully, false otherwise.
+ */
+ bool addKey(PARCKey *key);
+
+ PARCKeyId *addKeyFromCertificate(const std::string &file_name);
+
+ /**
+ * @brief Verify a Signature
+ *
+ * This method is general and must be used for Public-private key signature,
+ * HMAC and CRC.
+ *
+ * @param signature A pointer to the buffer holding the signature
+ * @param sign_len Lenght of the signature (must be consistent with the type
+ * of the key)
+ * @param bufferSigned A pointer to the packet header signed with
+ * signature. Mutable fields and the signature field in the packet must be
+ * set to 0
+ * @param buf_len Lenght of bufferSigned
+ * @param suite CryptoSuite to use to verify the signature
+ * @param key_id Indentifier of the key to use to verify the signature. The
+ * key must be already present in the Verifier.
+ */
+ int verify(const Packet &packet);
+
+ private:
+ PARCVerifier *verifier_;
+ static uint8_t zeros[200];
+};
+
+} // namespace utils