aboutsummaryrefslogtreecommitdiffstats
path: root/util/draft
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/draft
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/draft')
-rw-r--r--util/draft/date.cpp113
-rw-r--r--util/draft/date.h129
-rw-r--r--util/draft/date_ut.cpp36
-rw-r--r--util/draft/datetime.cpp237
-rw-r--r--util/draft/datetime.h184
-rw-r--r--util/draft/datetime_ut.cpp231
-rw-r--r--util/draft/enum.cpp1
-rw-r--r--util/draft/enum.h136
-rw-r--r--util/draft/holder_vector.cpp1
-rw-r--r--util/draft/holder_vector.h102
-rw-r--r--util/draft/holder_vector_ut.cpp69
-rw-r--r--util/draft/ip.cpp1
-rw-r--r--util/draft/ip.h131
-rw-r--r--util/draft/matrix.cpp1
-rw-r--r--util/draft/matrix.h108
-rw-r--r--util/draft/memory.cpp1
-rw-r--r--util/draft/memory.h40
-rw-r--r--util/draft/memory_ut.cpp69
-rw-r--r--util/draft/ut/ya.make21
-rw-r--r--util/draft/ya.make26
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
+)