diff --git a/tz.cpp b/tz.cpp index 32032f3..d346319 100644 --- a/tz.cpp +++ b/tz.cpp @@ -96,7 +96,6 @@ #include #include #include -#include #include #if USE_OS_TZDB # include @@ -320,21 +319,54 @@ struct undocumented {explicit undocumented() = default;}; static_assert(min_year <= max_year, "Configuration error"); #endif -static TZ_DB init_tzdb(); +static std::unique_ptr init_tzdb(); + +tzdb_list::~tzdb_list() +{ + const TZ_DB* ptr = head_; + head_ = nullptr; + while (ptr != nullptr) + { + auto next = ptr->next; + delete ptr; + ptr = next; + } +} + +tzdb_list::tzdb_list(tzdb_list&& x) noexcept + : head_{x.head_.exchange(nullptr)} +{ +} + +void +tzdb_list::push_front(TZ_DB* tzdb) noexcept +{ + tzdb->next = head_; + head_ = tzdb; +} + +tzdb_list::const_iterator +tzdb_list::erase_after(const_iterator p) noexcept +{ + auto t = p.p_->next; + p.p_->next = p.p_->next->next; + delete t; + return ++p; +} static -std::list +tzdb_list create_tzdb() { - std::list tz_db; - tz_db.push_back(init_tzdb()); + tzdb_list tz_db; + tz_db.push_front(init_tzdb().release()); return tz_db; } -std::list& +tzdb_list& get_tzdb_list() { - static std::list tz_db = create_tzdb(); + static tzdb_list tz_db = create_tzdb(); return tz_db; } @@ -2493,10 +2525,10 @@ get_version() # endif static -TZ_DB +std::unique_ptr init_tzdb() { - TZ_DB db; + std::unique_ptr db(new TZ_DB); //Iterate through folders std::queue subfolders; @@ -2535,22 +2567,22 @@ init_tzdb() } else { - db.zones.emplace_back(subname.substr(sizeof(tz_dir)), - detail::undocumented{}); + db->zones.emplace_back(subname.substr(sizeof(tz_dir)), + detail::undocumented{}); } } } closedir(dir); } - db.zones.shrink_to_fit(); - std::sort(db.zones.begin(), db.zones.end()); + db->zones.shrink_to_fit(); + std::sort(db->zones.begin(), db->zones.end()); # if !MISSING_LEAP_SECONDS std::ifstream in(tz_dir + std::string(1, folder_delimiter) + "right/UTC", std::ios_base::binary); if (in) { in.exceptions(std::ios::failbit | std::ios::badbit); - db.leaps = load_just_leaps(in); + db->leaps = load_just_leaps(in); } else { @@ -2559,11 +2591,11 @@ init_tzdb() if (!in) throw std::runtime_error("Unable to extract leap second information"); in.exceptions(std::ios::failbit | std::ios::badbit); - db.leaps = load_just_leaps(in); + db->leaps = load_just_leaps(in); } # endif // !MISSING_LEAP_SECONDS # ifdef __APPLE__ - db.version = get_version(); + db->version = get_version(); # endif return db; } @@ -3200,7 +3232,7 @@ get_version(const std::string& path) } static -TZ_DB +std::unique_ptr init_tzdb() { using namespace date; @@ -3208,7 +3240,7 @@ init_tzdb() const std::string path = install + folder_delimiter; std::string line; bool continue_zone = false; - TZ_DB db; + std::unique_ptr db(new TZ_DB); #if AUTO_DOWNLOAD if (!file_exists(install)) @@ -3233,18 +3265,18 @@ init_tzdb() msg += "\""; throw std::runtime_error(msg); } - db.version = get_version(path); + db->version = get_version(path); } else { - db.version = get_version(path); + db->version = get_version(path); auto rv = remote_version(); - if (!rv.empty() && db.version != rv) + if (!rv.empty() && db->version != rv) { if (remote_download(rv)) { remote_install(rv); - db.version = get_version(path); + db->version = get_version(path); } } } @@ -3256,7 +3288,7 @@ init_tzdb() msg += "\""; throw std::runtime_error(msg); } - db.version = get_version(path); + db->version = get_version(path); #endif // !AUTO_DOWNLOAD CONSTDATA char*const files[] = @@ -3278,27 +3310,27 @@ init_tzdb() in >> word; if (word == "Rule") { - db.rules.push_back(Rule(line)); + db->rules.push_back(Rule(line)); continue_zone = false; } else if (word == "Link") { - db.links.push_back(link(line)); + db->links.push_back(link(line)); continue_zone = false; } else if (word == "Leap") { - db.leaps.push_back(leap(line, detail::undocumented{})); + db->leaps.push_back(leap(line, detail::undocumented{})); continue_zone = false; } else if (word == "Zone") { - db.zones.push_back(time_zone(line, detail::undocumented{})); + db->zones.push_back(time_zone(line, detail::undocumented{})); continue_zone = true; } else if (line[0] == '\t' && continue_zone) { - db.zones.back().add(line); + db->zones.back().add(line); } else { @@ -3307,19 +3339,19 @@ init_tzdb() } } } - std::sort(db.rules.begin(), db.rules.end()); - Rule::split_overlaps(db.rules); - std::sort(db.zones.begin(), db.zones.end()); - db.zones.shrink_to_fit(); - std::sort(db.links.begin(), db.links.end()); - db.links.shrink_to_fit(); - std::sort(db.leaps.begin(), db.leaps.end()); - db.leaps.shrink_to_fit(); + std::sort(db->rules.begin(), db->rules.end()); + Rule::split_overlaps(db->rules); + std::sort(db->zones.begin(), db->zones.end()); + db->zones.shrink_to_fit(); + std::sort(db->links.begin(), db->links.end()); + db->links.shrink_to_fit(); + std::sort(db->leaps.begin(), db->leaps.end()); + db->leaps.shrink_to_fit(); #ifdef _WIN32 std::string mapping_file = get_install() + folder_delimiter + "windowsZones.xml"; - db.mappings = load_timezone_mappings_from_xml_file(mapping_file); - sort_zone_mappings(db.mappings); + db->mappings = load_timezone_mappings_from_xml_file(mapping_file); + sort_zone_mappings(db->mappings); #endif // _WIN32 return db; @@ -3333,7 +3365,7 @@ reload_tzdb() if (!v.empty() && v == remote_version()) return get_tzdb_list().front(); #endif // AUTO_DOWNLOAD - get_tzdb_list().push_front(init_tzdb()); + get_tzdb_list().push_front(init_tzdb().release()); return get_tzdb_list().front(); } diff --git a/tz.h b/tz.h index b0cc1d8..fb2667a 100644 --- a/tz.h +++ b/tz.h @@ -110,10 +110,10 @@ static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, #endif #include +#include #include #include #include -#include #include #include #include @@ -1144,6 +1144,7 @@ struct TZ_DB #ifdef _WIN32 std::vector mappings; #endif + TZ_DB* next = nullptr; TZ_DB() = default; #if !defined(_MSC_VER) || (_MSC_VER >= 1900) @@ -1183,7 +1184,92 @@ DATE_API std::ostream& operator<<(std::ostream& os, const TZ_DB& db); DATE_API const TZ_DB& get_tzdb(); -DATE_API std::list& get_tzdb_list(); + +class tzdb_list +{ + std::atomic head_{nullptr}; + +public: + ~tzdb_list(); + tzdb_list() = default; + tzdb_list(tzdb_list&& x) noexcept; + + void push_front(TZ_DB* tzdb) noexcept; + const TZ_DB& front() const noexcept {return *head_;} + + class const_iterator; + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + const_iterator erase_after(const_iterator p) noexcept; +}; + +class tzdb_list::const_iterator +{ + TZ_DB* p_ = nullptr; + + explicit const_iterator(TZ_DB* p) noexcept : p_{p} {} +public: + const_iterator() = default; + + using iterator_category = std::forward_iterator_tag; + using value_type = TZ_DB; + using reference = const value_type&; + using pointer = const value_type*; + using difference_type = std::ptrdiff_t; + + reference operator*() const noexcept {return *p_;} + pointer operator->() const noexcept {return p_;} + + const_iterator& operator++() noexcept {p_ = p_->next; return *this;} + const_iterator operator++(int) noexcept {auto t = *this; ++(*this); return t;} + + friend + bool + operator==(const const_iterator& x, const const_iterator& y) noexcept + {return x.p_ == y.p_;} + + friend + bool + operator!=(const const_iterator& x, const const_iterator& y) noexcept + {return !(x == y);} + + friend class tzdb_list; +}; + +inline +tzdb_list::const_iterator +tzdb_list::begin() const noexcept +{ + return const_iterator{head_}; +} + +inline +tzdb_list::const_iterator +tzdb_list::end() const noexcept +{ + return const_iterator{nullptr}; +} + +inline +tzdb_list::const_iterator +tzdb_list::cbegin() const noexcept +{ + return begin(); +} + +inline +tzdb_list::const_iterator +tzdb_list::cend() const noexcept +{ + return end(); +} + +DATE_API tzdb_list& get_tzdb_list(); #if !USE_OS_TZDB