diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/draft | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/draft')
-rw-r--r-- | util/draft/date.cpp | 113 | ||||
-rw-r--r-- | util/draft/date.h | 129 | ||||
-rw-r--r-- | util/draft/date_ut.cpp | 36 | ||||
-rw-r--r-- | util/draft/datetime.cpp | 237 | ||||
-rw-r--r-- | util/draft/datetime.h | 184 | ||||
-rw-r--r-- | util/draft/datetime_ut.cpp | 231 | ||||
-rw-r--r-- | util/draft/enum.cpp | 1 | ||||
-rw-r--r-- | util/draft/enum.h | 136 | ||||
-rw-r--r-- | util/draft/holder_vector.cpp | 1 | ||||
-rw-r--r-- | util/draft/holder_vector.h | 102 | ||||
-rw-r--r-- | util/draft/holder_vector_ut.cpp | 69 | ||||
-rw-r--r-- | util/draft/ip.cpp | 1 | ||||
-rw-r--r-- | util/draft/ip.h | 131 | ||||
-rw-r--r-- | util/draft/matrix.cpp | 1 | ||||
-rw-r--r-- | util/draft/matrix.h | 108 | ||||
-rw-r--r-- | util/draft/memory.cpp | 1 | ||||
-rw-r--r-- | util/draft/memory.h | 40 | ||||
-rw-r--r-- | util/draft/memory_ut.cpp | 69 | ||||
-rw-r--r-- | util/draft/ut/ya.make | 21 | ||||
-rw-r--r-- | util/draft/ya.make | 26 |
20 files changed, 1637 insertions, 0 deletions
diff --git a/util/draft/date.cpp b/util/draft/date.cpp new file mode 100644 index 0000000000..a290c46050 --- /dev/null +++ b/util/draft/date.cpp @@ -0,0 +1,113 @@ +#include "date.h" + +#include <util/string/cast.h> +#include <util/generic/yexception.h> +#include <util/datetime/base.h> + +time_t GetDateStart(time_t ts) { + tm dateTm; + memset(&dateTm, 0, sizeof(tm)); + localtime_r(&ts, &dateTm); + + dateTm.tm_isdst = -1; + + dateTm.tm_sec = 0; + dateTm.tm_min = 0; + dateTm.tm_hour = 0; + return mktime(&dateTm); +} + +static time_t ParseDate(const char* date, const char* format) { + tm dateTm; + memset(&dateTm, 0, sizeof(tm)); + if (!strptime(date, format, &dateTm)) { + ythrow yexception() << "Invalid date string and format: " << date << ", " << format; + } + return mktime(&dateTm); +} + +static time_t ParseDate(const char* dateStr) { + if (strlen(dateStr) != 8) { + ythrow yexception() << "Invalid date string: " << dateStr; + } + + return ParseDate(dateStr, "%Y%m%d"); +} + +template <> +TDate FromStringImpl<TDate>(const char* data, size_t len) { + return TDate(ParseDate(TString(data, len).data())); +} + +TDate::TDate(const char* yyyymmdd) + : Timestamp(GetDateStart(ParseDate(yyyymmdd))) +{ +} + +TDate::TDate(const TString& yyyymmdd) + : Timestamp(GetDateStart(ParseDate(yyyymmdd.c_str()))) +{ +} + +TDate::TDate(time_t ts) + : Timestamp(GetDateStart(ts)) +{ +} + +TDate::TDate(const TString& date, const TString& format) + : Timestamp(GetDateStart(ParseDate(date.data(), format.data()))) +{ +} + +TDate::TDate(unsigned year, unsigned month, unsigned monthDay) { + tm dateTm; + Zero(dateTm); + dateTm.tm_year = year - 1900; + dateTm.tm_mon = month - 1; + dateTm.tm_mday = monthDay; + dateTm.tm_isdst = -1; + Timestamp = mktime(&dateTm); + if (Timestamp == (time_t)-1) { + ythrow yexception() << "Invalid TDate args:(" << year << ',' << month << ',' << monthDay << ')'; + } +} + +time_t TDate::GetStartUTC() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + dateTm.tm_isdst = -1; + dateTm.tm_sec = 0; + dateTm.tm_min = 0; + dateTm.tm_hour = 0; + return TimeGM(&dateTm); +} + +TString TDate::ToStroka(const char* format) const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return Strftime(format, &dateTm); +} + +unsigned TDate::GetWeekDay() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return (unsigned)dateTm.tm_wday; +} + +unsigned TDate::GetYear() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return ((unsigned)dateTm.tm_year) + 1900; +} + +unsigned TDate::GetMonth() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return ((unsigned)dateTm.tm_mon) + 1; +} + +unsigned TDate::GetMonthDay() const { + tm dateTm; + localtime_r(&Timestamp, &dateTm); + return (unsigned)dateTm.tm_mday; +} diff --git a/util/draft/date.h b/util/draft/date.h new file mode 100644 index 0000000000..e3eb616fe5 --- /dev/null +++ b/util/draft/date.h @@ -0,0 +1,129 @@ +#pragma once + +#include <util/stream/output.h> +#include <util/stream/input.h> +#include <util/generic/string.h> +#include <util/datetime/constants.h> + +#include <ctime> + +// XXX: uses system calls for trivial things. may be very slow therefore. + +time_t GetDateStart(time_t ts); + +// Local date (without time zone) +class TDate { + // XXX: wrong: must store number of days since epoch + time_t Timestamp; + +public: + TDate() + : Timestamp(0) + { + } + + // XXX: wrong. Should be replace with two methods: TodayGmt() and TodayLocal() + static TDate Today() { + return TDate(time(nullptr)); + } + + TDate(const char* yyyymmdd); + TDate(const TString& yyyymmdd); + TDate(unsigned year, unsigned month, unsigned monthDay); // month from 01, monthDay from 01 + TDate(const TString& date, const TString& format); + + explicit TDate(time_t t); + + time_t GetStart() const { + return Timestamp; + } + + time_t GetStartUTC() const; + + TString ToStroka(const char* format = "%Y%m%d") const; + + TDate& operator++() { + Timestamp = GetDateStart(Timestamp + 3 * (SECONDS_IN_DAY / 2)); + return *this; + } + + TDate& operator--() { + Timestamp = GetDateStart(Timestamp - SECONDS_IN_DAY / 2); + return *this; + } + + TDate& operator+=(unsigned days) { + Timestamp = GetDateStart(Timestamp + days * SECONDS_IN_DAY + SECONDS_IN_DAY / 2); + return *this; + } + + TDate& operator-=(unsigned days) { + Timestamp = GetDateStart(Timestamp - days * SECONDS_IN_DAY + SECONDS_IN_DAY / 2); + return *this; + } + + TDate operator+(unsigned days) const { + return TDate(Timestamp + days * SECONDS_IN_DAY + SECONDS_IN_DAY / 2); + } + + TDate operator-(unsigned days) const { + return TDate(Timestamp - days * SECONDS_IN_DAY + SECONDS_IN_DAY / 2); + } + + unsigned GetWeekDay() const; // days since Sunday + + unsigned GetYear() const; + unsigned GetMonth() const; // from 01 + unsigned GetMonthDay() const; // from 01 + + friend bool operator<(const TDate& left, const TDate& right); + friend bool operator>(const TDate& left, const TDate& right); + friend bool operator<=(const TDate& left, const TDate& right); + friend bool operator>=(const TDate& left, const TDate& right); + friend bool operator==(const TDate& left, const TDate& right); + friend int operator-(const TDate& left, const TDate& right); + + friend IInputStream& operator>>(IInputStream& left, TDate& right); + friend IOutputStream& operator<<(IOutputStream& left, const TDate& right); +}; + +Y_DECLARE_PODTYPE(TDate); + +inline bool operator<(const TDate& left, const TDate& right) { + return left.Timestamp < right.Timestamp; +} + +inline bool operator>(const TDate& left, const TDate& right) { + return left.Timestamp > right.Timestamp; +} + +inline bool operator<=(const TDate& left, const TDate& right) { + return left.Timestamp <= right.Timestamp; +} + +inline bool operator>=(const TDate& left, const TDate& right) { + return left.Timestamp >= right.Timestamp; +} + +inline bool operator==(const TDate& left, const TDate& right) { + return left.Timestamp == right.Timestamp; +} + +inline int operator-(const TDate& left, const TDate& right) { + if (left < right) { + return -(right - left); + } + return static_cast<int>((left.Timestamp + SECONDS_IN_DAY / 2 - right.Timestamp) / SECONDS_IN_DAY); +} + +inline IInputStream& operator>>(IInputStream& left, TDate& right) { + TString stroka; + left >> stroka; + TDate date(stroka.c_str()); + right = date; + return left; +} + +inline IOutputStream& operator<<(IOutputStream& left, const TDate& right) { + return left << right.ToStroka(); +} diff --git a/util/draft/date_ut.cpp b/util/draft/date_ut.cpp new file mode 100644 index 0000000000..8c33a6c1cf --- /dev/null +++ b/util/draft/date_ut.cpp @@ -0,0 +1,36 @@ +#include "date.h" + +#include <library/cpp/testing/unittest/registar.h> + +Y_UNIT_TEST_SUITE(TDateTest) { + Y_UNIT_TEST(ComponentsTest) { + { + TDate d("20110215"); + UNIT_ASSERT_EQUAL(d.GetYear(), 2011); + UNIT_ASSERT_EQUAL(d.GetMonth(), 2); + UNIT_ASSERT_EQUAL(d.GetMonthDay(), 15); + UNIT_ASSERT_EQUAL(d.ToStroka("%Y%m%d"), "20110215"); + UNIT_ASSERT_EQUAL(d.ToStroka(), "20110215"); + UNIT_ASSERT_EQUAL(d.ToStroka("%Y--%m--%d"), "2011--02--15"); + UNIT_ASSERT_EQUAL(d.ToStroka("%U"), "07"); + UNIT_ASSERT_EQUAL(d.GetStartUTC(), 1297728000); + } + { + TDate d(2005, 6, 3); + UNIT_ASSERT_EQUAL(d.GetYear(), 2005); + UNIT_ASSERT_EQUAL(d.GetMonth(), 6); + UNIT_ASSERT_EQUAL(d.GetMonthDay(), 3); + UNIT_ASSERT_EQUAL(d.ToStroka(), "20050603"); + UNIT_ASSERT_EQUAL(d.ToStroka("____%Y__%m____%d"), "____2005__06____03"); + UNIT_ASSERT_EQUAL(d.GetStartUTC(), 1117756800); + } + { + TDate d("2011-02-15", "%Y-%m-%d"); + UNIT_ASSERT_EQUAL(d.GetYear(), 2011); + UNIT_ASSERT_EQUAL(d.GetMonth(), 2); + UNIT_ASSERT_EQUAL(d.GetMonthDay(), 15); + UNIT_ASSERT_EQUAL(d.ToStroka("%Y%m%d"), "20110215"); + UNIT_ASSERT_EQUAL(d.GetStartUTC(), 1297728000); + } + } +} diff --git a/util/draft/datetime.cpp b/util/draft/datetime.cpp new file mode 100644 index 0000000000..5cbe7d8847 --- /dev/null +++ b/util/draft/datetime.cpp @@ -0,0 +1,237 @@ +#include "datetime.h" + +#include <util/ysaveload.h> + +#include <util/system/atomic.h> +#include <util/system/fasttime.h> +#include <util/datetime/base.h> +#include <util/datetime/systime.h> +#include <util/stream/output.h> +#include <util/stream/mem.h> +#include <util/string/cast.h> +#include <util/string/printf.h> + +namespace NDatetime { + const ui32 MonthDays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, //nleap + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} //leap + }; + + const ui32 MonthDaysNewYear[2][13] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, //nleap + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} //leap + }; + + void YDayToMonthAndDay(ui32 yday, bool isleap, ui32* month, ui32* mday) { + const ui32* begin = MonthDaysNewYear[isleap] + 1; + const ui32* end = begin + 12; + // [31, ..., 365] or [31, ..., 366] (12 elements) + + const ui32* pos = UpperBound(begin, end, yday); + Y_ENSURE(pos != end, "day no. " << yday << " does not exist in " << (isleap ? "leap" : "non-leap") << " year"); + + *month = pos - begin; + *mday = yday - *(pos - 1) + 1; + + Y_ASSERT((*month < 12) && (1 <= *mday) && (*mday <= MonthDays[isleap][*month])); + } + + struct TTimeData { + i32 IsDst = 0; + i32 GMTOff = 0; + + TTimeData(time_t t) { + struct ::tm tt; + ::localtime_r(&t, &tt); +#ifndef _win_ + GMTOff = tt.tm_gmtoff; +#else + TIME_ZONE_INFORMATION tz; + switch (GetTimeZoneInformation(&tz)) { + case TIME_ZONE_ID_UNKNOWN: + GMTOff = tz.Bias * -60; + break; + case TIME_ZONE_ID_STANDARD: + GMTOff = (tz.Bias + tz.StandardBias) * -60; + break; + case TIME_ZONE_ID_DAYLIGHT: + GMTOff = (tz.Bias + tz.DaylightBias) * -60; + break; + default: + break; + } +#endif + IsDst = tt.tm_isdst; + } + }; + + TSimpleTM TSimpleTM::CurrentUTC() { + return New((time_t)TInstant::MicroSeconds(InterpolatedMicroSeconds()).Seconds()); + } + + TSimpleTM TSimpleTM::New(time_t t, i32 gmtoff, i8 isdst) { + time_t tt = t + gmtoff + isdst * 3600; + struct tm tmSys; + Zero(tmSys); + GmTimeR(&tt, &tmSys); + tmSys.tm_isdst = isdst; +#ifndef _win_ + tmSys.tm_gmtoff = gmtoff; +#endif + + return New(tmSys); + } + + TSimpleTM TSimpleTM::NewLocal(time_t t) { + TTimeData d(t); + return New(t, d.GMTOff, d.IsDst); + } + + TSimpleTM TSimpleTM::New(const struct tm& t) { + TSimpleTM res; + res.IsDst = t.tm_isdst; + res.Sec = t.tm_sec; + res.Min = t.tm_min; + res.Hour = t.tm_hour; + res.WDay = t.tm_wday; + res.Mon = t.tm_mon; + res.MDay = t.tm_mday; + res.Year = t.tm_year; + res.YDay = t.tm_yday; + res.IsLeap = LeapYearAD(res.Year + 1900); +#ifndef _win_ + res.GMTOff = t.tm_gmtoff; +#endif + return res; + } + + TSimpleTM& TSimpleTM::SetRealDate(ui32 year, ui32 mon, ui32 mday, ui32 hour, ui32 min, ui32 sec, i32 isdst) { + mday = ::Max<ui32>(mday, 1); + mon = ::Min<ui32>(::Max<ui32>(mon, 1), 12); + year = ::Max<ui32>(year, 1900); + + IsLeap = LeapYearAD(year); + Year = year - 1900; + Mon = mon - 1; + MDay = ::Min<ui32>(mday, MonthDays[IsLeap][Mon]); + Hour = Max<ui32>() == hour ? Hour : ::Min<ui32>(hour, 23); + Min = Max<ui32>() == min ? Min : ::Min<ui32>(min, 59); + Sec = Max<ui32>() == sec ? Sec : ::Min<ui32>(sec, 60); + IsDst = isdst; + + return RegenerateFields(); + } + + TSimpleTM& TSimpleTM::RegenerateFields() { + return *this = New(AsTimeT(), GMTOff, IsDst); + } + + TSimpleTM& TSimpleTM::Add(EField f, i32 amount) { + if (!amount) { + return *this; + } + + switch (f) { + default: + return *this; + case F_DAY: + amount *= 24; + [[fallthrough]]; + case F_HOUR: + amount *= 60; + [[fallthrough]]; + case F_MIN: + amount *= 60; + [[fallthrough]]; + case F_SEC: { + return *this = New(AsTimeT() + amount, GMTOff, IsDst); + } + case F_YEAR: { + i32 y = amount + (i32)Year; + y = ::Min<i32>(Max<i32>(y, 0), 255 /*max year*/); + + // YDay may correspond to different MDay if it's March or greater and the years have different leap status + if (Mon > 1) { + YDay += (i32)LeapYearAD(RealYear()) - (i32)LeapYearAD(RealYear()); + } + + Year = y; + IsLeap = LeapYearAD(RealYear()); + return RegenerateFields(); + } + case F_MON: { + i32 m = amount + Mon; + i32 y = (m < 0 ? (-12 + m) : m) / 12; + m = m - y * 12; + + if (y) { + Add(F_YEAR, y); + } + + if (m >= 0 && m < 12) { + MDay = ::Min<ui32>(MonthDays[IsLeap][m], MDay); + Mon = m; + } + + return RegenerateFields(); + } + } + } + + TString TSimpleTM::ToString(const char* fmt) const { + struct tm t = *this; + return Strftime(fmt, &t); + } + + time_t TSimpleTM::AsTimeT() const { + struct tm t = AsStructTmLocal(); + return TimeGM(&t) - GMTOff - IsDst * 3600; + } + + struct tm TSimpleTM::AsStructTmUTC() const { + struct tm res; + Zero(res); + time_t t = AsTimeT(); + return *GmTimeR(&t, &res); + } + + struct tm TSimpleTM::AsStructTmLocal() const { + struct tm t; + Zero(t); + t.tm_isdst = IsDst; + t.tm_sec = Sec; + t.tm_min = Min; + t.tm_hour = Hour; + t.tm_wday = WDay; + t.tm_mon = Mon; + t.tm_mday = MDay; + t.tm_year = Year; + t.tm_yday = YDay; +#ifndef _win_ + t.tm_gmtoff = GMTOff; +#endif + return t; + } +} + +template <> +void In<TMonth>(IInputStream& in, TMonth& t) { + char buf[4]; + LoadPodArray(&in, buf, 4); + t.Year = FromString<ui16>(buf, 4); + LoadPodArray(&in, buf, 2); + t.Month = ui8(FromString<ui16>(buf, 2)) - 1; +} + +template <> +void Out<TMonth>(IOutputStream& o, const TMonth& t) { + o << t.Year << Sprintf("%.2hu", (ui16)(t.Month + 1)); +} + +template <> +TMonth FromStringImpl<TMonth, char>(const char* s, size_t len) { + TMonth res; + TMemoryInput in(s, len); + in >> res; + return res; +} diff --git a/util/draft/datetime.h b/util/draft/datetime.h new file mode 100644 index 0000000000..8a387ea6f1 --- /dev/null +++ b/util/draft/datetime.h @@ -0,0 +1,184 @@ +#pragma once + +#include <util/generic/algorithm.h> +#include <util/generic/string.h> +#include <util/generic/yexception.h> +#include <util/generic/ymath.h> +#include <util/datetime/base.h> + +#include <cstdlib> + +#include <time.h> + +namespace NDatetime { + extern const ui32 MonthDays[2][12]; // !leapYear; !!leapYear + extern const ui32 MonthDaysNewYear[2][13]; // !leapYear; !!leapYear + + inline ui32 YearDaysAD(ui32 year) { + year = Max<ui32>(year, 1) - 1; //1 AD comes straight after 1 BC, no 0 AD + return year * 365 + year / 4 - year / 100 + year / 400; + } + + inline bool LeapYearAD(ui32 year) { + return (!(year % 4) && (year % 100)) || !(year % 400); + } + + inline ui32 YDayFromMonthAndDay(ui32 month /*0 - based*/, ui32 mday /*1 - based*/, bool isleap) { + return MonthDaysNewYear[isleap][Min(month, (ui32)11u)] + mday - 1; + } + + void YDayToMonthAndDay(ui32 yday /*0 - based*/, bool isleap, ui32* month /*0 - based*/, ui32* mday /*1 - based*/); + + struct TSimpleTM { + enum EField { + F_NONE = 0, + F_SEC, + F_MIN, + F_HOUR, + F_DAY, + F_MON, + F_YEAR + }; + + i32 GMTOff = 0; // -43200 - 50400 seconds + ui16 Year = 0; // from 1900 + ui16 YDay = 0; // 0-365 + ui8 Mon = 0; // 0-11 + ui8 MDay = 0; // 1-31 + ui8 WDay = 0; // 0-6 + ui8 Hour = 0; // 0-23 + ui8 Min = 0; // 0-59 + ui8 Sec = 0; // 0-60 - doesn't care for leap seconds. Most of the time it's ok. + i8 IsDst = 0; // -1/0/1 + bool IsLeap = false; + + public: + static TSimpleTM New(time_t t = 0, i32 gmtoff = 0, i8 isdst = 0); + static TSimpleTM NewLocal(time_t = 0); + + static TSimpleTM New(const struct tm&); + + static TSimpleTM CurrentUTC(); + + TSimpleTM() = default; + + TSimpleTM(ui32 year, ui32 mon, ui32 day, ui32 h = 0, ui32 m = 0, ui32 s = 0) { + Zero(*this); + SetRealDate(year, mon, day, h, m, s); + } + + // keeps the object consistent + TSimpleTM& Add(EField f, i32 amount = 1); + + TString ToString(const char* fmt = "%a, %d %b %Y %H:%M:%S %z") const; + + TSimpleTM& ToUTC() { + return *this = New(AsTimeT()); + } + + bool IsUTC() const { + return !IsDst && !GMTOff; + } + + time_t AsTimeT() const; + + operator time_t() const { + return AsTimeT(); + }; + + struct tm AsStructTmLocal() const; + + struct tm AsStructTmUTC() const; + + operator struct tm() const { + return AsStructTmLocal(); + } + + ui32 RealYear() const { + return ui32(Year + 1900); + } + + ui32 RealMonth() const { + return ui32(Mon + 1); + } + + TSimpleTM& SetRealDate(ui32 year, ui32 mon, ui32 mday, ui32 hour = -1, ui32 min = -1, ui32 sec = -1, i32 isdst = 0); + + // regenerates all fields from Year, MDay, Hour, Min, Sec, IsDst, GMTOffset + TSimpleTM& RegenerateFields(); + + friend bool operator==(const TSimpleTM& a, const TSimpleTM& b) { + return a.AsTimeT() == b.AsTimeT(); + } + + friend bool operator==(const TSimpleTM& s, const struct tm& t) { + return s == New(t); + } + + friend bool operator==(const struct tm& t, const TSimpleTM& s) { + return s == t; + } + + friend bool operator!=(const TSimpleTM& a, const TSimpleTM& b) { + return !(a == b); + } + + friend bool operator!=(const TSimpleTM& s, const struct tm& t) { + return !(s == t); + } + + friend bool operator!=(const struct tm& t, const TSimpleTM& s) { + return s != t; + } + }; +} + +inline TString date2str(const time_t date) { + struct tm dateTm; + memset(&dateTm, 0, sizeof(dateTm)); + localtime_r(&date, &dateTm); + char buf[9]; + strftime(buf, sizeof(buf), "%Y%m%d", &dateTm); + return TString(buf); +} + +inline time_t str2date(const TString& dateStr) { + struct tm dateTm; + memset(&dateTm, 0, sizeof(tm)); + strptime(dateStr.data(), "%Y%m%d", &dateTm); + return mktime(&dateTm); +} + +// checks whether time2 > time1 and close enough to it +inline bool AreTimesSeqAndClose(time_t time1, time_t time2, time_t closeInterval = 10) { + return (time2 - time1) <= closeInterval; +} + +// checks whether time2 and time1 are close enough +inline bool AreTimesClose(time_t time1, time_t time2, time_t closeInterval = 10) { + return std::abs(time2 - time1) <= closeInterval; +} + +//////////////////////////////// + +struct TMonth { + ui16 Year; + ui8 Month; + + TMonth(ui16 year = 0, ui8 month = 0) + : Year(year) + , Month(month) + { + } + + TMonth operator-(ui16 n) { + if (n <= Month) { + return TMonth(Year, Month - (ui8)n); + } else { + n -= Month; + return (n % 12) ? TMonth(Year - 1 - (n / 12), 12 - (n % 12)) : TMonth(Year - (n / 12), 0); + } + } +}; + +Y_DECLARE_PODTYPE(NDatetime::TSimpleTM); diff --git a/util/draft/datetime_ut.cpp b/util/draft/datetime_ut.cpp new file mode 100644 index 0000000000..a5e065ef6e --- /dev/null +++ b/util/draft/datetime_ut.cpp @@ -0,0 +1,231 @@ +#include "datetime.h" + +#include <library/cpp/testing/unittest/registar.h> + +#include <util/string/builder.h> + +Y_UNIT_TEST_SUITE(TSimpleTMTest) { + TString PrintMarker(const TString& test, int line) { + return TStringBuilder() << "test " << test << " at line " << line; + } + + TString JoinMarker(const TString& marker, time_t t) { + return TStringBuilder() << marker << " (tstamp=" << t << ")"; + } + + TString PrintMarker(const TString& test, int line, time_t t) { + return JoinMarker(PrintMarker(test, line), t); + } + + void AssertStructTmEqual(const TString& marker, const struct tm& tmt, const NDatetime::TSimpleTM& tms) { + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Sec, tmt.tm_sec, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Min, tmt.tm_min, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Hour, tmt.tm_hour, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.WDay, tmt.tm_wday, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.MDay, tmt.tm_mday, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Mon, tmt.tm_mon, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.YDay, tmt.tm_yday, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Year, tmt.tm_year, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.IsDst, tmt.tm_isdst, marker); +#ifndef _win_ + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.GMTOff, tmt.tm_gmtoff, marker); +#endif + } + + void AssertSimpleTM(const TString& mark, + const NDatetime::TSimpleTM& tms, + time_t tstamp, ui32 year, ui32 mon, ui32 mday, ui32 hour, ui32 minu, ui32 sec) { + TString marker = JoinMarker(mark, tstamp); + struct tm tmt; + Zero(tmt); + GmTimeR(&tstamp, &tmt); + AssertStructTmEqual(marker, tmt, tms); + tmt = tms.AsStructTmUTC(); + time_t tstamp1 = TimeGM(&tmt); + UNIT_ASSERT_VALUES_EQUAL_C(tstamp, tstamp1, marker); + UNIT_ASSERT_VALUES_EQUAL_C(tstamp, tms.AsTimeT(), marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.RealYear(), year, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.RealMonth(), mon, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.MDay, mday, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Hour, hour, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Min, minu, marker); + UNIT_ASSERT_VALUES_EQUAL_C((int)tms.Sec, sec, marker); + } + + Y_UNIT_TEST(TestLeap) { + using namespace NDatetime; + UNIT_ASSERT(LeapYearAD(2000)); + UNIT_ASSERT(LeapYearAD(2012)); + UNIT_ASSERT(!LeapYearAD(1999)); + UNIT_ASSERT(LeapYearAD(2004)); + UNIT_ASSERT(!LeapYearAD(1900)); + } + + Y_UNIT_TEST(TestYDayConversion) { + using namespace NDatetime; + ui32 month; + ui32 mday; + + for (ui32 yday = 0; yday < 365; ++yday) { + YDayToMonthAndDay(yday, false, &month, &mday); + UNIT_ASSERT_VALUES_EQUAL(yday, YDayFromMonthAndDay(month, mday, false)); + } + for (ui32 yday = 0; yday < 366; ++yday) { + YDayToMonthAndDay(yday, true, &month, &mday); + UNIT_ASSERT_VALUES_EQUAL(yday, YDayFromMonthAndDay(month, mday, true)); + } + + UNIT_ASSERT_EXCEPTION(YDayToMonthAndDay(365, false, &month, &mday), yexception); + UNIT_ASSERT_EXCEPTION(YDayToMonthAndDay(366, true, &month, &mday), yexception); + } + + Y_UNIT_TEST(SimpleTMTest) { + using namespace NDatetime; + + tzset(); + + TSimpleTM::New(-1); //should not die here + + UNIT_ASSERT_VALUES_EQUAL((ui32)0, (ui32)TSimpleTM::New(0)); + UNIT_ASSERT((ui32)TSimpleTM::New(0).IsUTC()); + time_t t = time(nullptr); + + { + struct tm tmt; + Zero(tmt); + gmtime_r(&t, &tmt); + UNIT_ASSERT_VALUES_EQUAL_C((i64)t, (i64)TSimpleTM::New(t).AsTimeT(), ToString(t)); // time_t -> gmt tm -> time_t + UNIT_ASSERT_VALUES_EQUAL_C((i64)t, (i64)TSimpleTM::New(tmt).AsTimeT(), ToString(t)); // gmt tm -> time_t + AssertStructTmEqual(PrintMarker("UTC:time_t", __LINE__, t), + tmt, TSimpleTM::New(t)); + AssertStructTmEqual(PrintMarker("UTC:tm", __LINE__, t), + tmt, TSimpleTM::New(tmt)); + UNIT_ASSERT(TSimpleTM::New(t).IsUTC()); + UNIT_ASSERT(TSimpleTM::New(tmt).IsUTC()); + } + + { + struct tm tmt; + Zero(tmt); + localtime_r(&t, &tmt); + + UNIT_ASSERT_VALUES_EQUAL((i64)t, (i64)TSimpleTM::NewLocal(t).AsTimeT()); // time_t -> local tm -> time_t + UNIT_ASSERT_VALUES_EQUAL((i64)t, (i64)TSimpleTM::New(tmt).AsTimeT()); + AssertStructTmEqual(PrintMarker("local:time_t", __LINE__, t), + tmt, TSimpleTM::NewLocal(t)); + AssertStructTmEqual(PrintMarker("local:tm", __LINE__, t), + tmt, TSimpleTM::New(tmt)); + AssertStructTmEqual(PrintMarker("local:tm:RegenerateFields", __LINE__, t), + tmt, TSimpleTM::New(tmt).RegenerateFields()); + AssertStructTmEqual(PrintMarker("local:time_t:SetRealDate", __LINE__, t), + tmt, TSimpleTM::NewLocal(t).SetRealDate(tmt.tm_year + 1900, tmt.tm_mon + 1, tmt.tm_mday, tmt.tm_hour, tmt.tm_min, tmt.tm_sec, tmt.tm_isdst)); + } + + { + TSimpleTM tt = TSimpleTM::New(0); + + tt.SetRealDate(2012, 3, 30, 5, 6, 7); + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1333083967, 2012, 3, 30, 5, 6, 7); + + tt.SetRealDate(2012, 3, 8, 5, 6, 7); + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1331183167, 2012, 3, 8, 5, 6, 7); + + tt.SetRealDate(2010, 10, 4, 5, 6, 7); + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1286168767, 2010, 10, 4, 5, 6, 7); + + tt.Add(TSimpleTM::F_MON); + AssertSimpleTM(PrintMarker("UTC:AddMonth", __LINE__), + tt, 1288847167, 2010, 11, 4, 5, 6, 7); + + tt.Add(TSimpleTM::F_DAY); + AssertSimpleTM(PrintMarker("UTC:AddDay", __LINE__), + tt, 1288933567, 2010, 11, 5, 5, 6, 7); + + tt.Add(TSimpleTM::F_YEAR); + AssertSimpleTM(PrintMarker("UTC:AddYear", __LINE__), + tt, 1320469567, 2011, 11, 5, 5, 6, 7); + + for (ui32 i = 0; i < 365; ++i) { + tt.Add(TSimpleTM::F_DAY); + } + + AssertSimpleTM(PrintMarker("UTC:365*AddDay", __LINE__), + tt, 1352005567, 2012, 11, 4, 5, 6, 7); + + tt.Add(TSimpleTM::F_MON, -10); + AssertSimpleTM(PrintMarker("UTC:AddMonth(-10)", __LINE__), + tt, 1325653567, 2012, 1, 4, 5, 6, 7); + + tt.Add(TSimpleTM::F_HOUR, -24 * 4 - 6); + AssertSimpleTM(PrintMarker("UTC:AddHour(-102)", __LINE__), + tt, 1325286367, 2011, 12, 30, 23, 6, 7); + } + + { + TSimpleTM tt = TSimpleTM::New(); + + tt.SetRealDate(2012, 2, 29); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1330473600, 2012, 2, 29, 0, 0, 0); + + tt.SetRealDate(2012, 2, 29); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1330473600, 2012, 2, 29, 0, 0, 0); + + tt.SetRealDate(2013, 12, 28); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1388188800, 2013, 12, 28, 0, 0, 0); + + tt.SetRealDate(2012, 10, 23); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1350950400, 2012, 10, 23, 0, 0, 0); + + tt.SetRealDate(2013, 3, 16); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1363392000, 2013, 3, 16, 0, 0, 0); + + tt.SetRealDate(2013, 2, 17); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1361059200, 2013, 2, 17, 0, 0, 0); + + tt.SetRealDate(2012, 12, 23); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1356220800, 2012, 12, 23, 0, 0, 0); + + tt.SetRealDate(2012, 5, 17); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1337212800, 2012, 5, 17, 0, 0, 0); + + tt.SetRealDate(2012, 6, 15); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1339718400, 2012, 6, 15, 0, 0, 0); + + tt.SetRealDate(2009, 3, 17); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1237248000, 2009, 3, 17, 0, 0, 0); + + tt.SetRealDate(2013, 8, 12); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1376265600, 2013, 8, 12, 0, 0, 0); + + tt.SetRealDate(2015, 12, 11, 10, 9, 8); + + AssertSimpleTM(PrintMarker("UTC:SetRealDate", __LINE__), + tt, 1449828548, 2015, 12, 11, 10, 9, 8); + } + } +} diff --git a/util/draft/enum.cpp b/util/draft/enum.cpp new file mode 100644 index 0000000000..40c79fb679 --- /dev/null +++ b/util/draft/enum.cpp @@ -0,0 +1 @@ +#include "enum.h" diff --git a/util/draft/enum.h b/util/draft/enum.h new file mode 100644 index 0000000000..18002b7df2 --- /dev/null +++ b/util/draft/enum.h @@ -0,0 +1,136 @@ +#pragma once + +#include <bitset> + +#include <util/generic/strbuf.h> +#include <util/stream/str.h> +#include <util/string/cast.h> +#include <util/string/split.h> +#include <utility> + +class TEnumNotFoundException: public yexception { +}; + +#define EnumFromString(key, entries) EnumFromStringImpl(key, entries, Y_ARRAY_SIZE(entries)) +#define EnumFromStringWithSize(key, entries, size) EnumFromStringImpl(key, entries, size) +#define FindEnumFromString(key, entries) FindEnumFromStringImpl(key, entries, Y_ARRAY_SIZE(entries)) +#define FindEnumFromStringWithSize(key, entries, size) FindEnumFromStringImpl(key, entries, size) +#define EnumToString(key, entries) EnumToStringImpl(key, entries, Y_ARRAY_SIZE(entries)) +#define EnumToStringWithSize(key, entries, size) EnumToStringImpl(key, entries, size) +#define PrintEnumItems(entries) PrintEnumItemsImpl(entries, Y_ARRAY_SIZE(entries)) + +template <class K1, class K2, class V> +const V* FindEnumFromStringImpl(K1 key, const std::pair<K2, V>* entries, size_t arraySize) { + for (size_t i = 0; i < arraySize; i++) + if (entries[i].first == key) + return &entries[i].second; + return nullptr; +} + +// special version for const char* +template <class V> +const V* FindEnumFromStringImpl(const char* key, const std::pair<const char*, V>* entries, size_t arraySize) { + for (size_t i = 0; i < arraySize; i++) + if (entries[i].first && key && !strcmp(entries[i].first, key)) + return &entries[i].second; + return nullptr; +} + +template <class K, class V> +TString PrintEnumItemsImpl(const std::pair<K, V>* entries, size_t arraySize) { + TString result; + TStringOutput out(result); + for (size_t i = 0; i < arraySize; i++) + out << (i ? ", " : "") << "'" << entries[i].first << "'"; + return result; +} + +// special version for const char* +template <class V> +TString PrintEnumItemsImpl(const std::pair<const char*, V>* entries, size_t arraySize) { + TString result; + TStringOutput out(result); + for (size_t i = 0; i < arraySize; i++) + out << (i ? ", " : "") << "'" << (entries[i].first ? entries[i].first : "<null>") << "'"; + return result; +} + +template <class K1, class K2, class V> +const V* EnumFromStringImpl(K1 key, const std::pair<K2, V>* entries, size_t arraySize) { + const V* res = FindEnumFromStringImpl(key, entries, arraySize); + if (res) + return res; + + ythrow TEnumNotFoundException() << "Key '" << key << "' not found in enum. Valid options are: " << PrintEnumItemsImpl(entries, arraySize) << ". "; +} + +template <class K, class V> +const K* EnumToStringImpl(V value, const std::pair<K, V>* entries, size_t arraySize) { + for (size_t i = 0; i < arraySize; i++) + if (entries[i].second == value) + return &entries[i].first; + + TEnumNotFoundException exc; + exc << "Value '" << int(value) << "' not found in enum. Valid values are: "; + for (size_t i = 0; i < arraySize; i++) + exc << (i ? ", " : "") << int(entries[i].second); + exc << ". "; + ythrow exc; +} + +/////////////////////////////////// + +template <class B> +inline void SetEnumFlagsForEmptySpec(B& flags, bool allIfEmpty) { + if (allIfEmpty) { + flags.set(); + } else { + flags.reset(); + } +} + +// all set by default +template <class E, size_t N, size_t B> +inline void SetEnumFlags(const std::pair<const char*, E> (&str2Enum)[N], TStringBuf optSpec, + std::bitset<B>& flags, bool allIfEmpty = true) { + if (optSpec.empty()) { + SetEnumFlagsForEmptySpec(flags, allIfEmpty); + } else { + flags.reset(); + for (const auto& it : StringSplitter(optSpec).Split(',')) { + E e = *EnumFromStringImpl(ToString(it.Token()).data(), str2Enum, N); + flags.set(e); + } + } +} + +template <class E, size_t B> +inline void SetEnumFlags(const std::pair<const char*, E>* str2Enum, TStringBuf optSpec, + std::bitset<B>& flags, const size_t size, + bool allIfEmpty = true) { + if (optSpec.empty()) { + SetEnumFlagsForEmptySpec(flags, allIfEmpty); + } else { + flags.reset(); + for (const auto& it : StringSplitter(optSpec).Split(',')) { + E e = *EnumFromStringImpl(ToString(it.Token()).data(), str2Enum, size); + flags.set(e); + } + } +} + +// for enums generated with GENERATE_ENUM_SERIALIZATION +template <class E, size_t B> +inline void SetEnumFlags(TStringBuf optSpec, std::bitset<B>& flags, bool allIfEmpty = true) { + if (optSpec.empty()) { + SetEnumFlagsForEmptySpec(flags, allIfEmpty); + } else { + flags.reset(); + for (const auto& it : StringSplitter(optSpec).Split(',')) { + E e; + if (!TryFromString(it.Token(), e)) + ythrow yexception() << "Unknown enum value '" << it.Token() << "'"; + flags.set((size_t)e); + } + } +} diff --git a/util/draft/holder_vector.cpp b/util/draft/holder_vector.cpp new file mode 100644 index 0000000000..9994c2a2b5 --- /dev/null +++ b/util/draft/holder_vector.cpp @@ -0,0 +1 @@ +#include "holder_vector.h" diff --git a/util/draft/holder_vector.h b/util/draft/holder_vector.h new file mode 100644 index 0000000000..1c62055bd9 --- /dev/null +++ b/util/draft/holder_vector.h @@ -0,0 +1,102 @@ +#pragma once + +#include <util/generic/ptr.h> +#include <util/generic/vector.h> +#include <util/generic/noncopyable.h> + +template <class T, class D = TDelete> +class THolderVector: public TVector<T*>, public TNonCopyable { + using TBase = TVector<T*>; + +public: + explicit THolderVector(size_t n = 0) + : TBase(n) + { + } + + ~THolderVector() { + Clear(); + } + + void Clear() { + for (typename TBase::iterator it = TBase::begin(); it != TBase::end(); ++it) { + if (*it) + D::Destroy(*it); + } + TBase::clear(); + } + + size_t Size() const { + return TBase::size(); + } + + // TVector takes ownership of T + void PushBack(T* t) { + try { + TBase::push_back(t); + } catch (...) { + if (t) + D::Destroy(t); + throw; + } + } + + void PushBack(std::unique_ptr<T> t) { + PushBack(t.release()); + } + + void PushBack(THolder<T> t) { + PushBack(t.Release()); + } + + void Reset(size_t i, THolder<T> t) { + T* current = (*this)[i]; + if (current) { + Y_ASSERT(current != t.Get()); + D::Destroy(current); + } + (*this)[i] = t.Release(); + } + + void PopBack() { + if (size()) { + D::Destroy(back()); + TBase::pop_back(); + } + } + + T* Release(size_t i) { + T* t = (*this)[i]; + (*this)[i] = nullptr; + return t; + } + + void Resize(size_t newSize) { + for (size_t i = newSize; i < size(); ++i) { + D::Destroy((*this)[i]); + } + TBase::resize(newSize); + } + + void Swap(THolderVector& other) { + TBase::swap(other); + } + + using TBase::operator[]; + using TBase::operator bool; + using TBase::at; + using TBase::back; + using TBase::begin; + using TBase::capacity; + using TBase::empty; + using TBase::end; + using TBase::front; + using TBase::reserve; + using TBase::size; + + using typename TBase::const_iterator; + using typename TBase::const_reverse_iterator; + using typename TBase::iterator; + using typename TBase::reverse_iterator; + using typename TBase::value_type; +}; diff --git a/util/draft/holder_vector_ut.cpp b/util/draft/holder_vector_ut.cpp new file mode 100644 index 0000000000..f64393860a --- /dev/null +++ b/util/draft/holder_vector_ut.cpp @@ -0,0 +1,69 @@ +#include "holder_vector.h" + +#include <library/cpp/testing/unittest/registar.h> + +Y_UNIT_TEST_SUITE(THolderVectorTest) { + Y_UNIT_TEST(TestCreateEmpty) { + THolderVector<int> ints; + UNIT_ASSERT_EQUAL(ints.Size(), 0); + UNIT_ASSERT(!ints); + } + + Y_UNIT_TEST(TestCreateNonEmpty) { + THolderVector<int> ints(5); + UNIT_ASSERT_EQUAL(ints.Size(), 5); + UNIT_ASSERT(ints); + + for (size_t i = 0; i < ints.Size(); ++i) { + UNIT_ASSERT_EQUAL(ints[i], (int*)nullptr); + } + } + + Y_UNIT_TEST(TestResetValue) { + THolderVector<int> ints; + ints.PushBack(new int(0)); + ints.PushBack(new int(1)); + UNIT_ASSERT_VALUES_EQUAL(*ints[0], 0); + UNIT_ASSERT_VALUES_EQUAL(*ints[1], 1); + ints.Reset(0, MakeHolder<int>(2)); + ints.Reset(1, MakeHolder<int>(3)); + UNIT_ASSERT_VALUES_EQUAL(*ints[0], 2); + UNIT_ASSERT_VALUES_EQUAL(*ints[1], 3); + } + + Y_UNIT_TEST(TestResetNoValue) { + THolderVector<int> ints; + ints.Resize(1); + UNIT_ASSERT_EQUAL(ints[0], (int*)nullptr); + ints.Reset(0, MakeHolder<int>(1)); + UNIT_ASSERT_UNEQUAL(ints[0], (int*)nullptr); + UNIT_ASSERT_VALUES_EQUAL(*ints[0], 1); + } + + Y_UNIT_TEST(TestResetSmartPtr) { + THolderVector<int> ints; + ints.Resize(2); + + THolder<int> holder(new int(1)); + ints.Reset(0, std::move(holder)); + UNIT_ASSERT_VALUES_EQUAL(*ints[0], 1); + UNIT_ASSERT(!holder); + } + + Y_UNIT_TEST(TestSwap) { + THolderVector<int> v1; + v1.PushBack(new int(1)); + + THolderVector<int> v2; + v1.Swap(v2); + UNIT_ASSERT(v1.empty() && v2.size() == 1 && *v2.front() == 1); + } + + Y_UNIT_TEST(TestUniquePtr) { + THolderVector<TString> v; + std::unique_ptr<TString> str(new TString("hello")); + v.PushBack(std::move(str)); + UNIT_ASSERT(v.Size() == 1); + UNIT_ASSERT(str.get() == nullptr); + } +} diff --git a/util/draft/ip.cpp b/util/draft/ip.cpp new file mode 100644 index 0000000000..a43bcdadcf --- /dev/null +++ b/util/draft/ip.cpp @@ -0,0 +1 @@ +#include "ip.h" diff --git a/util/draft/ip.h b/util/draft/ip.h new file mode 100644 index 0000000000..eb947cd2cd --- /dev/null +++ b/util/draft/ip.h @@ -0,0 +1,131 @@ +#pragma once + +#include <util/digest/murmur.h> + +#include <util/network/ip.h> + +#include <util/str_stl.h> +#include <util/generic/maybe.h> +#include <util/generic/variant.h> + +#ifdef _unix_ + #include <sys/types.h> + #include <sys/socket.h> + #include <arpa/inet.h> +#endif // _unix_ + +#include <string.h> + +#ifndef INET6_ADDRSTRLEN + #define INET6_ADDRSTRLEN 46 +#endif + +// Network (big-endian) byte order +using TIp4 = TIpHost; + +// Network (big-endian) byte order +struct TIp6 { + char Data[16]; + + bool operator==(const TIp6& rhs) const { + return memcmp(Data, rhs.Data, sizeof(Data)) == 0; + } + + bool operator<(const TIp6& rhs) const { + return memcmp(Data, rhs.Data, sizeof(Data)) < 0; + } +}; + +template <> +struct THash<TIp6> { + inline size_t operator()(const TIp6& ip) const { + return MurmurHash<size_t>((const void*)ip.Data, 16); + } +}; + +static inline TIp6 Ip6FromIp4(TIp4 addr) { + TIp6 res; + memset(res.Data, 0, sizeof(res.Data)); + res.Data[10] = '\xFF'; + res.Data[11] = '\xFF'; + memcpy(res.Data + 12, &addr, 4); + return res; +} + +static inline TIp6 Ip6FromString(const char* ipStr) { + TIp6 res; + + if (inet_pton(AF_INET6, ipStr, &res.Data) == 0) { + ythrow TSystemError() << "Failed to convert (" << ipStr << ") to ipv6 address"; + } + + return res; +} + +static inline TMaybe<TIp6> TryParseIp6FromString(const char* ipStr) { + TIp6 res; + + if (inet_pton(AF_INET6, ipStr, &res.Data) == 0) { + return Nothing(); + } + + return res; +} + +static inline char* Ip6ToString(const TIp6& ip, char* buf, size_t len) { + if (!inet_ntop(AF_INET6, (void*)&ip.Data, buf, (socklen_t)len)) { + ythrow TSystemError() << "Failed to get ipv6 address string"; + } + + return buf; +} + +static inline TString Ip6ToString(const TIp6& ip) { + char buf[INET6_ADDRSTRLEN]; + + return TString(Ip6ToString(ip, buf, sizeof(buf))); +} + +template <> +inline void Out<TIp6>(IOutputStream& os, const TIp6& a) { + os << Ip6ToString(a); +} + +using TIp4Or6 = std::variant<TIp4, TIp6>; + +static inline TIp4Or6 Ip4Or6FromString(const char* ipStr) { + const char* c = ipStr; + for (; *c; ++c) { + if (*c == '.') { + return IpFromString(ipStr); + } + if (*c == ':') { + return Ip6FromString(ipStr); + } + } + ythrow TSystemError() << "Failed to convert (" << ipStr << ") to ipv4 or ipv6 address"; +} + +static inline TString Ip4Or6ToString(const TIp4Or6& ip) { + if (std::holds_alternative<TIp6>(ip)) { + return Ip6ToString(std::get<TIp6>(ip)); + } else { + return IpToString(std::get<TIp4>(ip)); + } +} + +// for TIp4 or TIp6, not TIp4Or6 +template <class TIp> +struct TIpCompare { + bool Less(const TIp& l, const TIp& r) const { + return memcmp(&l, &r, sizeof(TIp)) < 0; + } + + bool LessEqual(const TIp& l, const TIp& r) const { + return memcmp(&l, &r, sizeof(TIp)) <= 0; + } + + bool operator()(const TIp& l, const TIp& r) const { + return Less(l, r); + } +}; diff --git a/util/draft/matrix.cpp b/util/draft/matrix.cpp new file mode 100644 index 0000000000..24a274b810 --- /dev/null +++ b/util/draft/matrix.cpp @@ -0,0 +1 @@ +#include "matrix.h" diff --git a/util/draft/matrix.h b/util/draft/matrix.h new file mode 100644 index 0000000000..154d00b35e --- /dev/null +++ b/util/draft/matrix.h @@ -0,0 +1,108 @@ +#pragma once + +#include <util/generic/noncopyable.h> +#include <util/system/yassert.h> +#include <util/system/defaults.h> +#include <string.h> + +template <typename T> +class TMatrix: TNonCopyable { + // Constructor/Destructor +public: + TMatrix() + : Buf(nullptr) + , Arr(nullptr) + , M(0) + , N(0) + , BufSize(0) + { + } + + TMatrix(size_t m, size_t n) + : Buf(new T[m * n]) + , Arr(Buf) + , M(m) + , N(n) + , BufSize(m * n) + { + } + + TMatrix(size_t m, size_t n, T* buf) + : Buf(nullptr) + , Arr(buf) + , M(m) + , N(n) + , BufSize(m * n) + { + } + + ~TMatrix() { + delete[] Buf; + } + + // Properties/Methods +public: + void Clear() { + M = N = 0; + } + + void ReDim(size_t m, size_t n) { + Y_ASSERT(m >= 1 && n >= 1); + size_t newSize = m * n; + if (newSize > BufSize) { + T* newBuf = new T[newSize]; + delete[] Buf; + Arr = Buf = newBuf; + BufSize = newSize; + } + M = m; + N = n; + } + + size_t Width() const { + return N; + } + + size_t Height() const { + return M; + } + + // Access element matrix[i][j] + T* operator[](size_t i) { + Y_ASSERT(i >= 0 && i < M); + return Arr + i * N; + } + + // Access element matrix[i][j] + const T* operator[](size_t i) const { + Y_ASSERT(i >= 0 && i < M); + return Arr + i * N; + } + + // Access element matrix(i, j) + T& operator()(size_t i, size_t j) { + Y_ASSERT(i >= 0 && i < M && j >= 0 && j < N); + return Arr[i * N + j]; + } + + // Access element matrix(i, j) + const T& operator()(size_t i, size_t j) const { + Y_ASSERT(i >= 0 && i < M && j >= 0 && j < N); + return Arr[i * N + j]; + } + + void Zero() { + memset((void*)Arr, 0, M * N * sizeof(T)); + } + + void Fill(T value) { + for (T *p = Arr, *end = Arr + M * N; p < end; ++p) + *p = value; + } + +private: + T* Buf; + T* Arr; + size_t M, N; + size_t BufSize; +}; diff --git a/util/draft/memory.cpp b/util/draft/memory.cpp new file mode 100644 index 0000000000..b31569d449 --- /dev/null +++ b/util/draft/memory.cpp @@ -0,0 +1 @@ +#include "memory.h" diff --git a/util/draft/memory.h b/util/draft/memory.h new file mode 100644 index 0000000000..0a9722bb36 --- /dev/null +++ b/util/draft/memory.h @@ -0,0 +1,40 @@ +#pragma once + +#include <util/system/defaults.h> + +#include <algorithm> +#include <functional> +#include <utility> + +template <class T> +inline bool IsZero(const T* begin, const T* end) { + return std::find_if(begin, end, [](const T& other) { return other != T(0); }) == end; +} + +template <size_t Size> +inline bool IsZero(const char* p) { + size_t sizeInUI64 = Size / 8; + const char* pEndUi64 = p + sizeInUI64 * 8; + if (sizeInUI64 && !IsZero<ui64>((const ui64*)p, (const ui64*)pEndUi64)) + return false; + return IsZero(pEndUi64, p + Size); +} + +#define IS_ZERO_INTSZ(INT) \ + template <> \ + inline bool IsZero<sizeof(INT)>(const char* p) { \ + return (*(INT*)p) == INT(0); \ + } + +IS_ZERO_INTSZ(ui8) +IS_ZERO_INTSZ(ui16) +IS_ZERO_INTSZ(ui32) +IS_ZERO_INTSZ(ui64) + +#undef IS_ZERO_INTSZ + +// If you want to use this to check all fields in a struct make sure it's w/o holes or #pragma pack(1) +template <class T> +bool IsZero(const T& t) { + return IsZero<sizeof(T)>((const char*)&t); +} diff --git a/util/draft/memory_ut.cpp b/util/draft/memory_ut.cpp new file mode 100644 index 0000000000..76bee30549 --- /dev/null +++ b/util/draft/memory_ut.cpp @@ -0,0 +1,69 @@ +#include "memory.h" + +#include <library/cpp/testing/unittest/registar.h> + +#pragma pack(1) +struct Y_PACKED TSampleStruct1 { + ui8 A; + ui8 B; +}; + +#pragma pack(1) +struct Y_PACKED TSampleStruct2 { + ui8 A; + ui16 B; + i32 C; +}; + +#pragma pack(1) +struct Y_PACKED TSampleStruct3 { + TSampleStruct2 A; + ui64 B; +}; + +#pragma pack() + +Y_UNIT_TEST_SUITE(TUtilDraftMemoryTest) { + Y_UNIT_TEST(IsZeroTest) { + ui8 a1 = 0; + UNIT_ASSERT(IsZero(a1)); + a1 = 0xF0; + UNIT_ASSERT(!IsZero(a1)); + + i32 a2 = -1; + UNIT_ASSERT(!IsZero(a2)); + a2 = 0; + UNIT_ASSERT(IsZero(a2)); + + double a3 = 0.0; + UNIT_ASSERT(IsZero(a3)); + a3 = 1.e-13; + UNIT_ASSERT(!IsZero(a3)); + + TSampleStruct1 ss1; + ss1.A = 0; + ss1.B = 0; + UNIT_ASSERT(IsZero(ss1)); + ss1.A = 0; + ss1.B = 12; + UNIT_ASSERT(!IsZero(ss1)); + + TSampleStruct2 ss2; + ss2.A = 0; + ss2.B = 100; + ss2.C = 0; + UNIT_ASSERT(!IsZero(ss2)); + ss2.B = 0; + UNIT_ASSERT(IsZero(ss2)); + + TSampleStruct3 ss3; + ss3.A = ss2; + ss3.B = 0; + UNIT_ASSERT(IsZero(ss3)); + ss3.B = 0x030; + UNIT_ASSERT(!IsZero(ss3)); + ss3.B = 0; + ss3.A.C = -789; + UNIT_ASSERT(!IsZero(ss3)); + } +} diff --git a/util/draft/ut/ya.make b/util/draft/ut/ya.make new file mode 100644 index 0000000000..37ab9413c5 --- /dev/null +++ b/util/draft/ut/ya.make @@ -0,0 +1,21 @@ +UNITTEST() + +OWNER(g:util) +SUBSCRIBER(g:util-subscribers) + +SRCDIR(util/draft) + +PEERDIR( + util/draft +) + +SRCS( + date_ut.cpp + datetime_ut.cpp + holder_vector_ut.cpp + memory_ut.cpp +) + +INCLUDE(${ARCADIA_ROOT}/util/tests/ya_util_tests.inc) + +END() diff --git a/util/draft/ya.make b/util/draft/ya.make new file mode 100644 index 0000000000..e00674b682 --- /dev/null +++ b/util/draft/ya.make @@ -0,0 +1,26 @@ +LIBRARY() + +OWNER(g:util) +SUBSCRIBER(g:util-subscribers) + +NO_UTIL() + +IF (TSTRING_IS_STD_STRING) + CFLAGS(GLOBAL -DTSTRING_IS_STD_STRING) +ENDIF() + +SRCS( + date.cpp + datetime.cpp + enum.cpp + holder_vector.cpp + ip.cpp + matrix.cpp + memory.cpp +) + +END() + +RECURSE_FOR_TESTS( + ut +) |