aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/ipmath/ipmath.cpp
diff options
context:
space:
mode:
authormsherbakov <msherbakov@yandex-team.ru>2022-02-10 16:49:17 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:49:17 +0300
commita0ffafe83b7d6229709a32fa942c71d672ac989c (patch)
tree5d5cb817648f650d76cf1076100726fd9b8448e8 /library/cpp/ipmath/ipmath.cpp
parentc224a621661ddd69699f9476922eb316607ef57e (diff)
downloadydb-a0ffafe83b7d6229709a32fa942c71d672ac989c.tar.gz
Restoring authorship annotation for <msherbakov@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'library/cpp/ipmath/ipmath.cpp')
-rw-r--r--library/cpp/ipmath/ipmath.cpp708
1 files changed, 354 insertions, 354 deletions
diff --git a/library/cpp/ipmath/ipmath.cpp b/library/cpp/ipmath/ipmath.cpp
index ccda3bdc7c..b8cca00c80 100644
--- a/library/cpp/ipmath/ipmath.cpp
+++ b/library/cpp/ipmath/ipmath.cpp
@@ -1,357 +1,357 @@
-#include "ipmath.h"
-
-namespace {
- constexpr auto IPV4_BITS = 32;
- constexpr auto IPV6_BITS = 128;
-
- const ui128 MAX_IPV4_ADDR = Max<ui32>();
- const ui128 MAX_IPV6_ADDR = Max<ui128>();
-
- TStringBuf TypeToString(TIpv6Address::TIpType type) {
- switch (type) {
- case TIpv6Address::Ipv4:
+#include "ipmath.h"
+
+namespace {
+ constexpr auto IPV4_BITS = 32;
+ constexpr auto IPV6_BITS = 128;
+
+ const ui128 MAX_IPV4_ADDR = Max<ui32>();
+ const ui128 MAX_IPV6_ADDR = Max<ui128>();
+
+ TStringBuf TypeToString(TIpv6Address::TIpType type) {
+ switch (type) {
+ case TIpv6Address::Ipv4:
return TStringBuf("IPv4");
- case TIpv6Address::Ipv6:
+ case TIpv6Address::Ipv6:
return TStringBuf("IPv6");
- default:
+ default:
return TStringBuf("UNKNOWN");
- }
- }
-
- size_t MaxPrefixLenForType(TIpv6Address::TIpType type) {
- switch (type) {
- case TIpv6Address::Ipv4:
- return IPV4_BITS;
- case TIpv6Address::Ipv6:
- return IPV6_BITS;
- case TIpv6Address::LAST:
- ythrow yexception() << "invalid type";
- }
- }
-
- template <ui8 ADDR_LEN>
- ui128 LowerBoundForPrefix(ui128 value, ui8 prefixLen) {
- const int shift = ADDR_LEN - prefixLen;
- const ui128 shifted = (shift < 128) ? (ui128{1} << shift) : 0;
- ui128 mask = ~(shifted - 1);
- return value & mask;
- }
-
- template <ui8 ADDR_LEN>
- ui128 UpperBoundForPrefix(ui128 value, ui8 prefixLen) {
- const int shift = ADDR_LEN - prefixLen;
- const ui128 shifted = (shift < 128) ? (ui128{1} << shift) : 0;
- ui128 mask = shifted - 1;
- return value | mask;
- }
-
- auto LowerBoundForPrefix4 = LowerBoundForPrefix<IPV4_BITS>;
- auto LowerBoundForPrefix6 = LowerBoundForPrefix<IPV6_BITS>;
- auto UpperBoundForPrefix4 = UpperBoundForPrefix<IPV4_BITS>;
- auto UpperBoundForPrefix6 = UpperBoundForPrefix<IPV6_BITS>;
-
- TIpv6Address IpFromStringSafe(const TString& s) {
- bool ok{};
- auto addr = TIpv6Address::FromString(s, ok);
- Y_ENSURE(ok, "Failed to parse an IP address from " << s);
- return addr;
- }
-
- /// it's different from TIpv6Address::IsValid for 0.0.0.0
- bool IsValid(TIpv6Address addr) {
- switch (addr.Type()) {
- case TIpv6Address::Ipv4:
- case TIpv6Address::Ipv6:
- return true;
-
- case TIpv6Address::LAST:
- return false;
- }
- }
-
- bool HasNext(TIpv6Address addr) {
- switch (addr.Type()) {
- case TIpv6Address::Ipv4:
- return ui128(addr) != MAX_IPV4_ADDR;
- case TIpv6Address::Ipv6:
- return ui128(addr) != MAX_IPV6_ADDR;
- case TIpv6Address::LAST:
- return false;
- }
- }
-
- TIpv6Address Next(TIpv6Address addr) {
- return {ui128(addr) + 1, addr.Type()};
- }
-} // namespace
-
-TIpv6Address LowerBoundForPrefix(TIpv6Address value, ui8 prefixLen) {
- auto type = value.Type();
- switch (type) {
- case TIpv6Address::Ipv4:
- return {LowerBoundForPrefix4(value, prefixLen), type};
- case TIpv6Address::Ipv6:
- return {LowerBoundForPrefix6(value, prefixLen), type};
- default:
- ythrow yexception() << "invalid type";
- }
-}
-
-TIpv6Address UpperBoundForPrefix(TIpv6Address value, ui8 prefixLen) {
- auto type = value.Type();
- switch (type) {
- case TIpv6Address::Ipv4:
- return {UpperBoundForPrefix4(value, prefixLen), type};
- case TIpv6Address::Ipv6:
- return {UpperBoundForPrefix6(value, prefixLen), type};
- default:
- ythrow yexception() << "invalid type";
- }
-}
-
-TIpAddressRange::TIpAddressRangeBuilder::operator TIpAddressRange() {
- return Build();
-}
-
-TIpAddressRange TIpAddressRange::TIpAddressRangeBuilder::Build() {
- return TIpAddressRange{Start_, End_};
-}
-
-TIpAddressRange::TIpAddressRangeBuilder::TIpAddressRangeBuilder(const TString& from)
- : TIpAddressRangeBuilder{IpFromStringSafe(from)}
-{
-}
-
-TIpAddressRange::TIpAddressRangeBuilder::TIpAddressRangeBuilder(TIpv6Address from) {
- Y_ENSURE_EX(IsValid(from), TInvalidIpRangeException() << "Address " << from.ToString() << " is invalid");
- Start_ = from;
- End_ = Start_;
-}
-
-TIpAddressRange::TIpAddressRangeBuilder& TIpAddressRange::TIpAddressRangeBuilder::To(const TString& to) {
- End_ = IpFromStringSafe(to);
- return *this;
-}
-
-TIpAddressRange::TIpAddressRangeBuilder& TIpAddressRange::TIpAddressRangeBuilder::To(TIpv6Address to) {
- Y_ENSURE_EX(IsValid(to), TInvalidIpRangeException() << "Address " << to.ToString() << " is invalid");
- Start_ = to;
- return *this;
-}
-
-TIpAddressRange::TIpAddressRangeBuilder& TIpAddressRange::TIpAddressRangeBuilder::WithPrefix(ui8 len) {
- Y_ENSURE_EX(IsValid(Start_), TInvalidIpRangeException() << "Start value must be set before prefix");
- const auto type = Start_.Type();
- const auto maxLen = MaxPrefixLenForType(type);
- Y_ENSURE_EX(len <= maxLen, TInvalidIpRangeException() << "Maximum prefix length for this address type is "
- << maxLen << ", but requested " << (ui32)len);
-
- const auto lowerBound = LowerBoundForPrefix(Start_, len);
- Y_ENSURE_EX(Start_ == lowerBound, TInvalidIpRangeException() << "Cannot create IP range from start address "
- << Start_ << " with prefix length " << (ui32)len);
-
- End_ = UpperBoundForPrefix(Start_, len);
-
- return *this;
-}
-
-void TIpAddressRange::Init(TIpv6Address from, TIpv6Address to) {
- Start_ = from;
- End_ = to;
-
- Y_ENSURE_EX(Start_ <= End_, TInvalidIpRangeException() << "Invalid IP address range: from " << Start_ << " to " << End_);
- Y_ENSURE_EX(Start_.Type() == End_.Type(), TInvalidIpRangeException()
- << "Address type mismtach: start address type is " << TypeToString(Start_.Type())
- << " end type is " << TypeToString(End_.Type()));
-}
-
-TIpAddressRange::TIpAddressRange(TIpv6Address start, TIpv6Address end) {
- Y_ENSURE_EX(IsValid(start), TInvalidIpRangeException() << "start address " << start.ToString() << " is invalid");
- Y_ENSURE_EX(IsValid(end), TInvalidIpRangeException() << "end address " << end.ToString() << " is invalid");
- Init(start, end);
-}
-
-TIpAddressRange::TIpAddressRange(const TString& start, const TString& end) {
- auto startAddr = IpFromStringSafe(start);
- auto endAddr = IpFromStringSafe(end);
- Init(startAddr, endAddr);
-}
-
-TIpAddressRange::~TIpAddressRange() {
-}
-
-TIpAddressRange::TIpType TIpAddressRange::Type() const {
- return Start_.Type();
-}
-
-ui128 TIpAddressRange::Size() const {
- return ui128(End_) - ui128(Start_) + 1;
-}
-
-bool TIpAddressRange::IsSingle() const {
- return Start_ == End_;
-}
-
-bool TIpAddressRange::Contains(const TIpAddressRange& other) const {
- return Start_ <= other.Start_ && End_ >= other.End_;
-}
-
-bool TIpAddressRange::Contains(const TIpv6Address& addr) const {
- return Start_ <= addr && End_ >= addr;
-}
-
-bool TIpAddressRange::Overlaps(const TIpAddressRange& other) const {
- return Start_ <= other.End_ && other.Start_ <= End_;
-}
-
-bool TIpAddressRange::IsConsecutive(const TIpAddressRange& other) const {
- return (HasNext(End_) && Next(End_) == other.Start_)
- || (HasNext(other.End_) && Next(other.End_) == Start_);
-}
-
-TIpAddressRange TIpAddressRange::Union(const TIpAddressRange& other) const {
- Y_ENSURE(IsConsecutive(other) || Overlaps(other), "Can merge only consecutive or overlapping ranges");
- Y_ENSURE(other.Start_.Type() == Start_.Type(), "Cannot merge ranges of addresses of different types");
-
- auto s = Start_;
- auto e = End_;
-
- s = {Min<ui128>(Start_, other.Start_), Start_.Type()};
- e = {Max<ui128>(End_, other.End_), End_.Type()};
-
- return {s, e};
-}
-
-TIpAddressRange TIpAddressRange::FromCidrString(const TString& str) {
- if (auto result = TryFromCidrString(str)) {
- return *result;
- }
-
- ythrow TInvalidIpRangeException() << "Cannot parse " << str << " as a CIDR string";
-}
-
-TMaybe<TIpAddressRange> TIpAddressRange::TryFromCidrString(const TString& str) {
- auto idx = str.rfind('/');
- if (idx == TString::npos) {
- return Nothing();
- }
-
- TStringBuf sb{str};
- TStringBuf address, prefix;
- sb.SplitAt(idx, address, prefix);
- prefix.Skip(1);
-
- ui8 prefixLen{};
- if (!::TryFromString(prefix, prefixLen)) {
- return Nothing();
- }
-
- return TIpAddressRange::From(TString{address})
- .WithPrefix(prefixLen);
-}
-
-TIpAddressRange TIpAddressRange::FromRangeString(const TString& str) {
- if (auto result = TryFromRangeString(str)) {
- return *result;
- }
-
- ythrow TInvalidIpRangeException() << "Cannot parse " << str << " as a range string";
-}
-
-TMaybe<TIpAddressRange> TIpAddressRange::TryFromRangeString(const TString& str) {
- auto idx = str.find('-');
- if (idx == TString::npos) {
- return Nothing();
- }
-
- TStringBuf sb{str};
- TStringBuf start, end;
- sb.SplitAt(idx, start, end);
- end.Skip(1);
-
- return TIpAddressRange::From(TString{start}).To(TString{end});
-}
-
-TIpAddressRange TIpAddressRange::FromString(const TString& str) {
- if (auto result = TryFromString(str)) {
- return *result;
- }
-
- ythrow TInvalidIpRangeException() << "Cannot parse an IP address from " << str;
-}
-
-TMaybe<TIpAddressRange> TIpAddressRange::TryFromString(const TString& str) {
- if (auto idx = str.find('/'); idx != TString::npos) {
- return TryFromCidrString(str);
- } else if (idx = str.find('-'); idx != TString::npos) {
- return TryFromRangeString(str);
- } else {
- bool ok{};
- auto addr = TIpv6Address::FromString(str, ok);
- if (!ok) {
- return Nothing();
- }
-
- return TIpAddressRange::From(addr);
- }
-}
-
-TString TIpAddressRange::ToRangeString() const {
- bool ok{};
- return TStringBuilder() << Start_.ToString(ok) << "-" << End_.ToString(ok);
-}
-
-TIpAddressRange::TIterator TIpAddressRange::begin() const {
- return Begin();
-}
-
-TIpAddressRange::TIterator TIpAddressRange::Begin() const {
- return TIpAddressRange::TIterator{Start_};
-}
-
-TIpAddressRange::TIterator TIpAddressRange::end() const {
- return End();
-}
-
-TIpAddressRange::TIterator TIpAddressRange::End() const {
- return TIpAddressRange::TIterator{{ui128(End_) + 1, End_.Type()}};
-}
-
-TIpAddressRange::TIpAddressRangeBuilder TIpAddressRange::From(TIpv6Address from) {
- return TIpAddressRangeBuilder{from};
-};
-
-TIpAddressRange::TIpAddressRangeBuilder TIpAddressRange::From(const TString& from) {
- return TIpAddressRangeBuilder{from};
-};
-
-bool operator==(const TIpAddressRange& lhs, const TIpAddressRange& rhs) {
- return lhs.Start_ == rhs.Start_ && lhs.End_ == rhs.End_;
-}
-
-bool operator!=(const TIpAddressRange& lhs, const TIpAddressRange& rhs) {
- return !(lhs == rhs);
-}
-
-TIpAddressRange::TIterator::TIterator(TIpv6Address val) noexcept
- : Current_{val}
-{
-}
-
-bool TIpAddressRange::TIterator::operator==(const TIpAddressRange::TIterator& other) noexcept {
- return Current_ == other.Current_;
-}
-
-bool TIpAddressRange::TIterator::operator!=(const TIpAddressRange::TIterator& other) noexcept {
- return !(*this == other);
-}
-
-TIpAddressRange::TIterator& TIpAddressRange::TIterator::operator++() noexcept {
- ui128 numeric = Current_;
- Current_ = {numeric + 1, Current_.Type()};
- return *this;
-}
-
-const TIpv6Address& TIpAddressRange::TIterator::operator*() noexcept {
- return Current_;
-}
+ }
+ }
+
+ size_t MaxPrefixLenForType(TIpv6Address::TIpType type) {
+ switch (type) {
+ case TIpv6Address::Ipv4:
+ return IPV4_BITS;
+ case TIpv6Address::Ipv6:
+ return IPV6_BITS;
+ case TIpv6Address::LAST:
+ ythrow yexception() << "invalid type";
+ }
+ }
+
+ template <ui8 ADDR_LEN>
+ ui128 LowerBoundForPrefix(ui128 value, ui8 prefixLen) {
+ const int shift = ADDR_LEN - prefixLen;
+ const ui128 shifted = (shift < 128) ? (ui128{1} << shift) : 0;
+ ui128 mask = ~(shifted - 1);
+ return value & mask;
+ }
+
+ template <ui8 ADDR_LEN>
+ ui128 UpperBoundForPrefix(ui128 value, ui8 prefixLen) {
+ const int shift = ADDR_LEN - prefixLen;
+ const ui128 shifted = (shift < 128) ? (ui128{1} << shift) : 0;
+ ui128 mask = shifted - 1;
+ return value | mask;
+ }
+
+ auto LowerBoundForPrefix4 = LowerBoundForPrefix<IPV4_BITS>;
+ auto LowerBoundForPrefix6 = LowerBoundForPrefix<IPV6_BITS>;
+ auto UpperBoundForPrefix4 = UpperBoundForPrefix<IPV4_BITS>;
+ auto UpperBoundForPrefix6 = UpperBoundForPrefix<IPV6_BITS>;
+
+ TIpv6Address IpFromStringSafe(const TString& s) {
+ bool ok{};
+ auto addr = TIpv6Address::FromString(s, ok);
+ Y_ENSURE(ok, "Failed to parse an IP address from " << s);
+ return addr;
+ }
+
+ /// it's different from TIpv6Address::IsValid for 0.0.0.0
+ bool IsValid(TIpv6Address addr) {
+ switch (addr.Type()) {
+ case TIpv6Address::Ipv4:
+ case TIpv6Address::Ipv6:
+ return true;
+
+ case TIpv6Address::LAST:
+ return false;
+ }
+ }
+
+ bool HasNext(TIpv6Address addr) {
+ switch (addr.Type()) {
+ case TIpv6Address::Ipv4:
+ return ui128(addr) != MAX_IPV4_ADDR;
+ case TIpv6Address::Ipv6:
+ return ui128(addr) != MAX_IPV6_ADDR;
+ case TIpv6Address::LAST:
+ return false;
+ }
+ }
+
+ TIpv6Address Next(TIpv6Address addr) {
+ return {ui128(addr) + 1, addr.Type()};
+ }
+} // namespace
+
+TIpv6Address LowerBoundForPrefix(TIpv6Address value, ui8 prefixLen) {
+ auto type = value.Type();
+ switch (type) {
+ case TIpv6Address::Ipv4:
+ return {LowerBoundForPrefix4(value, prefixLen), type};
+ case TIpv6Address::Ipv6:
+ return {LowerBoundForPrefix6(value, prefixLen), type};
+ default:
+ ythrow yexception() << "invalid type";
+ }
+}
+
+TIpv6Address UpperBoundForPrefix(TIpv6Address value, ui8 prefixLen) {
+ auto type = value.Type();
+ switch (type) {
+ case TIpv6Address::Ipv4:
+ return {UpperBoundForPrefix4(value, prefixLen), type};
+ case TIpv6Address::Ipv6:
+ return {UpperBoundForPrefix6(value, prefixLen), type};
+ default:
+ ythrow yexception() << "invalid type";
+ }
+}
+
+TIpAddressRange::TIpAddressRangeBuilder::operator TIpAddressRange() {
+ return Build();
+}
+
+TIpAddressRange TIpAddressRange::TIpAddressRangeBuilder::Build() {
+ return TIpAddressRange{Start_, End_};
+}
+
+TIpAddressRange::TIpAddressRangeBuilder::TIpAddressRangeBuilder(const TString& from)
+ : TIpAddressRangeBuilder{IpFromStringSafe(from)}
+{
+}
+
+TIpAddressRange::TIpAddressRangeBuilder::TIpAddressRangeBuilder(TIpv6Address from) {
+ Y_ENSURE_EX(IsValid(from), TInvalidIpRangeException() << "Address " << from.ToString() << " is invalid");
+ Start_ = from;
+ End_ = Start_;
+}
+
+TIpAddressRange::TIpAddressRangeBuilder& TIpAddressRange::TIpAddressRangeBuilder::To(const TString& to) {
+ End_ = IpFromStringSafe(to);
+ return *this;
+}
+
+TIpAddressRange::TIpAddressRangeBuilder& TIpAddressRange::TIpAddressRangeBuilder::To(TIpv6Address to) {
+ Y_ENSURE_EX(IsValid(to), TInvalidIpRangeException() << "Address " << to.ToString() << " is invalid");
+ Start_ = to;
+ return *this;
+}
+
+TIpAddressRange::TIpAddressRangeBuilder& TIpAddressRange::TIpAddressRangeBuilder::WithPrefix(ui8 len) {
+ Y_ENSURE_EX(IsValid(Start_), TInvalidIpRangeException() << "Start value must be set before prefix");
+ const auto type = Start_.Type();
+ const auto maxLen = MaxPrefixLenForType(type);
+ Y_ENSURE_EX(len <= maxLen, TInvalidIpRangeException() << "Maximum prefix length for this address type is "
+ << maxLen << ", but requested " << (ui32)len);
+
+ const auto lowerBound = LowerBoundForPrefix(Start_, len);
+ Y_ENSURE_EX(Start_ == lowerBound, TInvalidIpRangeException() << "Cannot create IP range from start address "
+ << Start_ << " with prefix length " << (ui32)len);
+
+ End_ = UpperBoundForPrefix(Start_, len);
+
+ return *this;
+}
+
+void TIpAddressRange::Init(TIpv6Address from, TIpv6Address to) {
+ Start_ = from;
+ End_ = to;
+
+ Y_ENSURE_EX(Start_ <= End_, TInvalidIpRangeException() << "Invalid IP address range: from " << Start_ << " to " << End_);
+ Y_ENSURE_EX(Start_.Type() == End_.Type(), TInvalidIpRangeException()
+ << "Address type mismtach: start address type is " << TypeToString(Start_.Type())
+ << " end type is " << TypeToString(End_.Type()));
+}
+
+TIpAddressRange::TIpAddressRange(TIpv6Address start, TIpv6Address end) {
+ Y_ENSURE_EX(IsValid(start), TInvalidIpRangeException() << "start address " << start.ToString() << " is invalid");
+ Y_ENSURE_EX(IsValid(end), TInvalidIpRangeException() << "end address " << end.ToString() << " is invalid");
+ Init(start, end);
+}
+
+TIpAddressRange::TIpAddressRange(const TString& start, const TString& end) {
+ auto startAddr = IpFromStringSafe(start);
+ auto endAddr = IpFromStringSafe(end);
+ Init(startAddr, endAddr);
+}
+
+TIpAddressRange::~TIpAddressRange() {
+}
+
+TIpAddressRange::TIpType TIpAddressRange::Type() const {
+ return Start_.Type();
+}
+
+ui128 TIpAddressRange::Size() const {
+ return ui128(End_) - ui128(Start_) + 1;
+}
+
+bool TIpAddressRange::IsSingle() const {
+ return Start_ == End_;
+}
+
+bool TIpAddressRange::Contains(const TIpAddressRange& other) const {
+ return Start_ <= other.Start_ && End_ >= other.End_;
+}
+
+bool TIpAddressRange::Contains(const TIpv6Address& addr) const {
+ return Start_ <= addr && End_ >= addr;
+}
+
+bool TIpAddressRange::Overlaps(const TIpAddressRange& other) const {
+ return Start_ <= other.End_ && other.Start_ <= End_;
+}
+
+bool TIpAddressRange::IsConsecutive(const TIpAddressRange& other) const {
+ return (HasNext(End_) && Next(End_) == other.Start_)
+ || (HasNext(other.End_) && Next(other.End_) == Start_);
+}
+
+TIpAddressRange TIpAddressRange::Union(const TIpAddressRange& other) const {
+ Y_ENSURE(IsConsecutive(other) || Overlaps(other), "Can merge only consecutive or overlapping ranges");
+ Y_ENSURE(other.Start_.Type() == Start_.Type(), "Cannot merge ranges of addresses of different types");
+
+ auto s = Start_;
+ auto e = End_;
+
+ s = {Min<ui128>(Start_, other.Start_), Start_.Type()};
+ e = {Max<ui128>(End_, other.End_), End_.Type()};
+
+ return {s, e};
+}
+
+TIpAddressRange TIpAddressRange::FromCidrString(const TString& str) {
+ if (auto result = TryFromCidrString(str)) {
+ return *result;
+ }
+
+ ythrow TInvalidIpRangeException() << "Cannot parse " << str << " as a CIDR string";
+}
+
+TMaybe<TIpAddressRange> TIpAddressRange::TryFromCidrString(const TString& str) {
+ auto idx = str.rfind('/');
+ if (idx == TString::npos) {
+ return Nothing();
+ }
+
+ TStringBuf sb{str};
+ TStringBuf address, prefix;
+ sb.SplitAt(idx, address, prefix);
+ prefix.Skip(1);
+
+ ui8 prefixLen{};
+ if (!::TryFromString(prefix, prefixLen)) {
+ return Nothing();
+ }
+
+ return TIpAddressRange::From(TString{address})
+ .WithPrefix(prefixLen);
+}
+
+TIpAddressRange TIpAddressRange::FromRangeString(const TString& str) {
+ if (auto result = TryFromRangeString(str)) {
+ return *result;
+ }
+
+ ythrow TInvalidIpRangeException() << "Cannot parse " << str << " as a range string";
+}
+
+TMaybe<TIpAddressRange> TIpAddressRange::TryFromRangeString(const TString& str) {
+ auto idx = str.find('-');
+ if (idx == TString::npos) {
+ return Nothing();
+ }
+
+ TStringBuf sb{str};
+ TStringBuf start, end;
+ sb.SplitAt(idx, start, end);
+ end.Skip(1);
+
+ return TIpAddressRange::From(TString{start}).To(TString{end});
+}
+
+TIpAddressRange TIpAddressRange::FromString(const TString& str) {
+ if (auto result = TryFromString(str)) {
+ return *result;
+ }
+
+ ythrow TInvalidIpRangeException() << "Cannot parse an IP address from " << str;
+}
+
+TMaybe<TIpAddressRange> TIpAddressRange::TryFromString(const TString& str) {
+ if (auto idx = str.find('/'); idx != TString::npos) {
+ return TryFromCidrString(str);
+ } else if (idx = str.find('-'); idx != TString::npos) {
+ return TryFromRangeString(str);
+ } else {
+ bool ok{};
+ auto addr = TIpv6Address::FromString(str, ok);
+ if (!ok) {
+ return Nothing();
+ }
+
+ return TIpAddressRange::From(addr);
+ }
+}
+
+TString TIpAddressRange::ToRangeString() const {
+ bool ok{};
+ return TStringBuilder() << Start_.ToString(ok) << "-" << End_.ToString(ok);
+}
+
+TIpAddressRange::TIterator TIpAddressRange::begin() const {
+ return Begin();
+}
+
+TIpAddressRange::TIterator TIpAddressRange::Begin() const {
+ return TIpAddressRange::TIterator{Start_};
+}
+
+TIpAddressRange::TIterator TIpAddressRange::end() const {
+ return End();
+}
+
+TIpAddressRange::TIterator TIpAddressRange::End() const {
+ return TIpAddressRange::TIterator{{ui128(End_) + 1, End_.Type()}};
+}
+
+TIpAddressRange::TIpAddressRangeBuilder TIpAddressRange::From(TIpv6Address from) {
+ return TIpAddressRangeBuilder{from};
+};
+
+TIpAddressRange::TIpAddressRangeBuilder TIpAddressRange::From(const TString& from) {
+ return TIpAddressRangeBuilder{from};
+};
+
+bool operator==(const TIpAddressRange& lhs, const TIpAddressRange& rhs) {
+ return lhs.Start_ == rhs.Start_ && lhs.End_ == rhs.End_;
+}
+
+bool operator!=(const TIpAddressRange& lhs, const TIpAddressRange& rhs) {
+ return !(lhs == rhs);
+}
+
+TIpAddressRange::TIterator::TIterator(TIpv6Address val) noexcept
+ : Current_{val}
+{
+}
+
+bool TIpAddressRange::TIterator::operator==(const TIpAddressRange::TIterator& other) noexcept {
+ return Current_ == other.Current_;
+}
+
+bool TIpAddressRange::TIterator::operator!=(const TIpAddressRange::TIterator& other) noexcept {
+ return !(*this == other);
+}
+
+TIpAddressRange::TIterator& TIpAddressRange::TIterator::operator++() noexcept {
+ ui128 numeric = Current_;
+ Current_ = {numeric + 1, Current_.Type()};
+ return *this;
+}
+
+const TIpv6Address& TIpAddressRange::TIterator::operator*() noexcept {
+ return Current_;
+}