v8/src/date.cc
Clemens Hammacher 5f6510825a [cleanup] Fix remaining (D)CHECK macro usages
This CL fixes all occurences that don't require special OWNER reviews,
or can be reviewed by Michi.

After this one, we should be able to reenable the readability/check
cpplint check.

R=mstarzinger@chromium.org

Bug: v8:6837, v8:6921
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng;master.tryserver.v8:v8_linux_noi18n_rel_ng
Change-Id: Ic81d68d5534eaa795b7197fed5c41ed158361d62
Reviewed-on: https://chromium-review.googlesource.com/721120
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48670}
2017-10-18 10:12:31 +00:00

393 lines
12 KiB
C++

// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/date.h"
#include "src/objects.h"
#include "src/objects-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/intl.h"
#endif
namespace v8 {
namespace internal {
static const int kDaysIn4Years = 4 * 365 + 1;
static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
static const int kDays1970to2000 = 30 * 365 + 7;
static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
kDays1970to2000;
static const int kYearsOffset = 400000;
static const char kDaysInMonths[] =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
DateCache::DateCache()
: stamp_(0),
tz_cache_(
#ifdef V8_INTL_SUPPORT
FLAG_icu_timezone_data ? new ICUTimezoneCache()
: base::OS::CreateTimezoneCache()
#else
base::OS::CreateTimezoneCache()
#endif
) {
ResetDateCache();
}
void DateCache::ResetDateCache() {
static const int kMaxStamp = Smi::kMaxValue;
if (stamp_->value() >= kMaxStamp) {
stamp_ = Smi::kZero;
} else {
stamp_ = Smi::FromInt(stamp_->value() + 1);
}
DCHECK(stamp_ != Smi::FromInt(kInvalidStamp));
for (int i = 0; i < kDSTSize; ++i) {
ClearSegment(&dst_[i]);
}
dst_usage_counter_ = 0;
before_ = &dst_[0];
after_ = &dst_[1];
local_offset_ms_ = kInvalidLocalOffsetInMs;
ymd_valid_ = false;
tz_cache_->Clear();
tz_name_ = nullptr;
dst_tz_name_ = nullptr;
}
void DateCache::ClearSegment(DST* segment) {
segment->start_sec = kMaxEpochTimeInSec;
segment->end_sec = -kMaxEpochTimeInSec;
segment->offset_ms = 0;
segment->last_used = 0;
}
void DateCache::YearMonthDayFromDays(
int days, int* year, int* month, int* day) {
if (ymd_valid_) {
// Check conservatively if the given 'days' has
// the same year and month as the cached 'days'.
int new_day = ymd_day_ + (days - ymd_days_);
if (new_day >= 1 && new_day <= 28) {
ymd_day_ = new_day;
ymd_days_ = days;
*year = ymd_year_;
*month = ymd_month_;
*day = new_day;
return;
}
}
int save_days = days;
days += kDaysOffset;
*year = 400 * (days / kDaysIn400Years) - kYearsOffset;
days %= kDaysIn400Years;
DCHECK_EQ(save_days, DaysFromYearMonth(*year, 0) + days);
days--;
int yd1 = days / kDaysIn100Years;
days %= kDaysIn100Years;
*year += 100 * yd1;
days++;
int yd2 = days / kDaysIn4Years;
days %= kDaysIn4Years;
*year += 4 * yd2;
days--;
int yd3 = days / 365;
days %= 365;
*year += yd3;
bool is_leap = (!yd1 || yd2) && !yd3;
DCHECK_GE(days, -1);
DCHECK(is_leap || (days >= 0));
DCHECK((days < 365) || (is_leap && (days < 366)));
DCHECK(is_leap == ((*year % 4 == 0) && (*year % 100 || (*year % 400 == 0))));
DCHECK(is_leap || ((DaysFromYearMonth(*year, 0) + days) == save_days));
DCHECK(!is_leap || ((DaysFromYearMonth(*year, 0) + days + 1) == save_days));
days += is_leap;
// Check if the date is after February.
if (days >= 31 + 28 + BoolToInt(is_leap)) {
days -= 31 + 28 + BoolToInt(is_leap);
// Find the date starting from March.
for (int i = 2; i < 12; i++) {
if (days < kDaysInMonths[i]) {
*month = i;
*day = days + 1;
break;
}
days -= kDaysInMonths[i];
}
} else {
// Check January and February.
if (days < 31) {
*month = 0;
*day = days + 1;
} else {
*month = 1;
*day = days - 31 + 1;
}
}
DCHECK(DaysFromYearMonth(*year, *month) + *day - 1 == save_days);
ymd_valid_ = true;
ymd_year_ = *year;
ymd_month_ = *month;
ymd_day_ = *day;
ymd_days_ = save_days;
}
int DateCache::DaysFromYearMonth(int year, int month) {
static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
181, 212, 243, 273, 304, 334};
static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
182, 213, 244, 274, 305, 335};
year += month / 12;
month %= 12;
if (month < 0) {
year--;
month += 12;
}
DCHECK_GE(month, 0);
DCHECK_LT(month, 12);
// year_delta is an arbitrary number such that:
// a) year_delta = -1 (mod 400)
// b) year + year_delta > 0 for years in the range defined by
// ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
// Jan 1 1970. This is required so that we don't run into integer
// division of negative numbers.
// c) there shouldn't be an overflow for 32-bit integers in the following
// operations.
static const int year_delta = 399999;
static const int base_day = 365 * (1970 + year_delta) +
(1970 + year_delta) / 4 -
(1970 + year_delta) / 100 +
(1970 + year_delta) / 400;
int year1 = year + year_delta;
int day_from_year = 365 * year1 +
year1 / 4 -
year1 / 100 +
year1 / 400 -
base_day;
if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) {
return day_from_year + day_from_month[month];
}
return day_from_year + day_from_month_leap[month];
}
void DateCache::BreakDownTime(int64_t time_ms, int* year, int* month, int* day,
int* weekday, int* hour, int* min, int* sec,
int* ms) {
int const days = DaysFromTime(time_ms);
int const time_in_day_ms = TimeInDay(time_ms, days);
YearMonthDayFromDays(days, year, month, day);
*weekday = Weekday(days);
*hour = time_in_day_ms / (60 * 60 * 1000);
*min = (time_in_day_ms / (60 * 1000)) % 60;
*sec = (time_in_day_ms / 1000) % 60;
*ms = time_in_day_ms % 1000;
}
void DateCache::ExtendTheAfterSegment(int time_sec, int offset_ms) {
if (after_->offset_ms == offset_ms &&
after_->start_sec <= time_sec + kDefaultDSTDeltaInSec &&
time_sec <= after_->end_sec) {
// Extend the after_ segment.
after_->start_sec = time_sec;
} else {
// The after_ segment is either invalid or starts too late.
if (after_->start_sec <= after_->end_sec) {
// If the after_ segment is valid, replace it with a new segment.
after_ = LeastRecentlyUsedDST(before_);
}
after_->start_sec = time_sec;
after_->end_sec = time_sec;
after_->offset_ms = offset_ms;
after_->last_used = ++dst_usage_counter_;
}
}
int DateCache::DaylightSavingsOffsetInMs(int64_t time_ms) {
int time_sec = (time_ms >= 0 && time_ms <= kMaxEpochTimeInMs)
? static_cast<int>(time_ms / 1000)
: static_cast<int>(EquivalentTime(time_ms) / 1000);
// Invalidate cache if the usage counter is close to overflow.
// Note that dst_usage_counter is incremented less than ten times
// in this function.
if (dst_usage_counter_ >= kMaxInt - 10) {
dst_usage_counter_ = 0;
for (int i = 0; i < kDSTSize; ++i) {
ClearSegment(&dst_[i]);
}
}
// Optimistic fast check.
if (before_->start_sec <= time_sec &&
time_sec <= before_->end_sec) {
// Cache hit.
before_->last_used = ++dst_usage_counter_;
return before_->offset_ms;
}
ProbeDST(time_sec);
DCHECK(InvalidSegment(before_) || before_->start_sec <= time_sec);
DCHECK(InvalidSegment(after_) || time_sec < after_->start_sec);
if (InvalidSegment(before_)) {
// Cache miss.
before_->start_sec = time_sec;
before_->end_sec = time_sec;
before_->offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
before_->last_used = ++dst_usage_counter_;
return before_->offset_ms;
}
if (time_sec <= before_->end_sec) {
// Cache hit.
before_->last_used = ++dst_usage_counter_;
return before_->offset_ms;
}
if (time_sec > before_->end_sec + kDefaultDSTDeltaInSec) {
// If the before_ segment ends too early, then just
// query for the offset of the time_sec
int offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
ExtendTheAfterSegment(time_sec, offset_ms);
// This swap helps the optimistic fast check in subsequent invocations.
DST* temp = before_;
before_ = after_;
after_ = temp;
return offset_ms;
}
// Now the time_sec is between
// before_->end_sec and before_->end_sec + default DST delta.
// Update the usage counter of before_ since it is going to be used.
before_->last_used = ++dst_usage_counter_;
// Check if after_ segment is invalid or starts too late.
// Note that start_sec of invalid segments is kMaxEpochTimeInSec.
if (before_->end_sec + kDefaultDSTDeltaInSec <= after_->start_sec) {
int new_after_start_sec = before_->end_sec + kDefaultDSTDeltaInSec;
int new_offset_ms = GetDaylightSavingsOffsetFromOS(new_after_start_sec);
ExtendTheAfterSegment(new_after_start_sec, new_offset_ms);
} else {
DCHECK(!InvalidSegment(after_));
// Update the usage counter of after_ since it is going to be used.
after_->last_used = ++dst_usage_counter_;
}
// Now the time_sec is between before_->end_sec and after_->start_sec.
// Only one daylight savings offset change can occur in this interval.
if (before_->offset_ms == after_->offset_ms) {
// Merge two segments if they have the same offset.
before_->end_sec = after_->end_sec;
ClearSegment(after_);
return before_->offset_ms;
}
// Binary search for daylight savings offset change point,
// but give up if we don't find it in five iterations.
for (int i = 4; i >= 0; --i) {
int delta = after_->start_sec - before_->end_sec;
int middle_sec = (i == 0) ? time_sec : before_->end_sec + delta / 2;
int offset_ms = GetDaylightSavingsOffsetFromOS(middle_sec);
if (before_->offset_ms == offset_ms) {
before_->end_sec = middle_sec;
if (time_sec <= before_->end_sec) {
return offset_ms;
}
} else {
DCHECK(after_->offset_ms == offset_ms);
after_->start_sec = middle_sec;
if (time_sec >= after_->start_sec) {
// This swap helps the optimistic fast check in subsequent invocations.
DST* temp = before_;
before_ = after_;
after_ = temp;
return offset_ms;
}
}
}
return 0;
}
void DateCache::ProbeDST(int time_sec) {
DST* before = nullptr;
DST* after = nullptr;
DCHECK(before_ != after_);
for (int i = 0; i < kDSTSize; ++i) {
if (dst_[i].start_sec <= time_sec) {
if (before == nullptr || before->start_sec < dst_[i].start_sec) {
before = &dst_[i];
}
} else if (time_sec < dst_[i].end_sec) {
if (after == nullptr || after->end_sec > dst_[i].end_sec) {
after = &dst_[i];
}
}
}
// If before or after segments were not found,
// then set them to any invalid segment.
if (before == nullptr) {
before = InvalidSegment(before_) ? before_ : LeastRecentlyUsedDST(after);
}
if (after == nullptr) {
after = InvalidSegment(after_) && before != after_
? after_ : LeastRecentlyUsedDST(before);
}
DCHECK_NOT_NULL(before);
DCHECK_NOT_NULL(after);
DCHECK(before != after);
DCHECK(InvalidSegment(before) || before->start_sec <= time_sec);
DCHECK(InvalidSegment(after) || time_sec < after->start_sec);
DCHECK(InvalidSegment(before) || InvalidSegment(after) ||
before->end_sec < after->start_sec);
before_ = before;
after_ = after;
}
DateCache::DST* DateCache::LeastRecentlyUsedDST(DST* skip) {
DST* result = nullptr;
for (int i = 0; i < kDSTSize; ++i) {
if (&dst_[i] == skip) continue;
if (result == nullptr || result->last_used > dst_[i].last_used) {
result = &dst_[i];
}
}
ClearSegment(result);
return result;
}
} // namespace internal
} // namespace v8