/* Copyright (c) 2018, BitTorrent Inc. Copyright (c) 2018, Steven Siloti Copyright (c) 2019-2022, Arvid Norberg Copyright (c) 2020, Mike Tzou All rights reserved. You may use, distribute and modify this code under the terms of the BSD license, see LICENSE file. */ #ifndef TORRENT_INFO_HASH_HPP_INCLUDED #define TORRENT_INFO_HASH_HPP_INCLUDED #include "libtorrent/config.hpp" #include "libtorrent/sha1_hash.hpp" #if TORRENT_USE_IOSTREAM #include #endif // TORRENT_USE_IOSTREAM namespace libtorrent { // BitTorrent version enumerator enum class protocol_version : std::uint8_t { // The original BitTorrent version, using SHA-1 hashes V1, // Version 2 of the BitTorrent protocol, using SHA-256 hashes V2, NUM }; // internal constexpr std::size_t num_protocols = int(protocol_version::NUM); namespace { std::initializer_list const all_versions{ protocol_version::V1, protocol_version::V2 }; } // class holding the info-hash of a torrent. It can hold a v1 info-hash // (SHA-1) or a v2 info-hash (SHA-256) or both. // // .. note:: // // If ``has_v2()`` is false then the v1 hash might actually be a truncated // v2 hash struct TORRENT_EXPORT info_hash_t { // The default constructor creates an object that has neither a v1 or v2 // hash. // // For backwards compatibility, make it possible to construct directly // from a v1 hash. This constructor allows *implicit* conversion from a // v1 hash, but the implicitness is deprecated. info_hash_t() noexcept = default; explicit info_hash_t(sha1_hash h1) noexcept : v1(h1) {} // NOLINT explicit info_hash_t(sha256_hash h2) noexcept : v2(h2) {} info_hash_t(sha1_hash h1, sha256_hash h2) noexcept : v1(h1), v2(h2) {} // hidden info_hash_t(info_hash_t const&) noexcept = default; info_hash_t& operator=(info_hash_t const&) & noexcept = default; // returns true if the corresponding info hash is present in this // object. bool has_v1() const { return !v1.is_all_zeros(); } bool has_v2() const { return !v2.is_all_zeros(); } bool has(protocol_version v) const { TORRENT_ASSERT(v != protocol_version::NUM); return v == protocol_version::V1 ? has_v1() : has_v2(); } // returns the has for the specified protocol version sha1_hash get(protocol_version v) const { TORRENT_ASSERT(v != protocol_version::NUM); return v == protocol_version::V1 ? v1 : sha1_hash(v2.data()); } // returns the v2 (truncated) info-hash, if there is one, otherwise // returns the v1 info-hash sha1_hash get_best() const { return has_v2() ? get(protocol_version::V2) : v1; } friend bool operator!=(info_hash_t const& lhs, info_hash_t const& rhs) { return std::tie(lhs.v1, lhs.v2) != std::tie(rhs.v1, rhs.v2); } friend bool operator==(info_hash_t const& lhs, info_hash_t const& rhs) noexcept { return std::tie(lhs.v1, lhs.v2) == std::tie(rhs.v1, rhs.v2); } // calls the function object ``f`` for each hash that is available. // starting with v1. The signature of ``F`` is:: // // void(sha1_hash const&, protocol_version); template void for_each(F f) const { if (has_v1()) f(v1, protocol_version::V1); if (has_v2()) f(sha1_hash(v2.data()), protocol_version::V2); } bool operator<(info_hash_t const& o) const { return std::tie(v1, v2) < std::tie(o.v1, o.v2); } #if TORRENT_USE_IOSTREAM friend std::ostream& operator<<(std::ostream& os, info_hash_t const& ih) { return os << '[' << ih.v1 << ',' << ih.v2 << ']'; } #endif // TORRENT_USE_IOSTREAM // the v1 (SHA-1) info-hash and the v2 (SHA-256) info-hash. For a // hybrid torrent both are populated. For a pure v1 or pure v2 // torrent, the unused field is the all-zero hash. sha1_hash v1; sha256_hash v2; }; } namespace std { template <> struct hash { std::size_t operator()(libtorrent::info_hash_t const& k) const { return std::hash{}(k.v1) ^ std::hash{}(k.v2) ; } }; } #endif