// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #include #include #include // Must go after other includes. #include namespace google { namespace protobuf { namespace util { using google::protobuf::Duration; using google::protobuf::Timestamp; namespace { static const int kNanosPerSecond = 1000000000; static const int kMicrosPerSecond = 1000000; static const int kMillisPerSecond = 1000; static const int kNanosPerMillisecond = 1000000; static const int kNanosPerMicrosecond = 1000; static const int kSecondsPerMinute = 60; // Note that we ignore leap seconds. static const int kSecondsPerHour = 3600; template T CreateNormalized(int64_t seconds, int64_t nanos); template <> Timestamp CreateNormalized(int64_t seconds, int64_t nanos) { // Make sure nanos is in the range. if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) { seconds += nanos / kNanosPerSecond; nanos = nanos % kNanosPerSecond; } // For Timestamp nanos should be in the range [0, 999999999] if (nanos < 0) { seconds -= 1; nanos += kNanosPerSecond; } GOOGLE_DCHECK(seconds >= TimeUtil::kTimestampMinSeconds && seconds <= TimeUtil::kTimestampMaxSeconds); Timestamp result; result.set_seconds(seconds); result.set_nanos(static_cast(nanos)); return result; } template <> Duration CreateNormalized(int64_t seconds, int64_t nanos) { // Make sure nanos is in the range. if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) { seconds += nanos / kNanosPerSecond; nanos = nanos % kNanosPerSecond; } // nanos should have the same sign as seconds. if (seconds < 0 && nanos > 0) { seconds += 1; nanos -= kNanosPerSecond; } else if (seconds > 0 && nanos < 0) { seconds -= 1; nanos += kNanosPerSecond; } GOOGLE_DCHECK(seconds >= TimeUtil::kDurationMinSeconds && seconds <= TimeUtil::kDurationMaxSeconds); Duration result; result.set_seconds(seconds); result.set_nanos(static_cast(nanos)); return result; } // Format nanoseconds with either 3, 6, or 9 digits depending on the required // precision to represent the exact value. std::string FormatNanos(int32_t nanos) { if (nanos % kNanosPerMillisecond == 0) { return StringPrintf("%03d", nanos / kNanosPerMillisecond); } else if (nanos % kNanosPerMicrosecond == 0) { return StringPrintf("%06d", nanos / kNanosPerMicrosecond); } else { return StringPrintf("%09d", nanos); } } std::string FormatTime(int64 seconds, int32 nanos) { return ::google::protobuf::internal::FormatTime(seconds, nanos); } bool ParseTime(const std::string& value, int64* seconds, int32* nanos) { return ::google::protobuf::internal::ParseTime(value, seconds, nanos); } void CurrentTime(int64* seconds, int32* nanos) { return ::google::protobuf::internal::GetCurrentTime(seconds, nanos); } // Truncates the remainder part after division. int64_t RoundTowardZero(int64_t value, int64_t divider) { int64_t result = value / divider; int64_t remainder = value % divider; // Before C++11, the sign of the remainder is implementation dependent if // any of the operands is negative. Here we try to enforce C++11's "rounded // toward zero" semantics. For example, for (-5) / 2 an implementation may // give -3 as the result with the remainder being 1. This function ensures // we always return -2 (closer to zero) regardless of the implementation. if (result < 0 && remainder > 0) { return result + 1; } else { return result; } } } // namespace // Actually define these static const integers. Required by C++ standard (but // some compilers don't like it). #ifndef _MSC_VER const int64_t TimeUtil::kTimestampMinSeconds; const int64_t TimeUtil::kTimestampMaxSeconds; const int64_t TimeUtil::kDurationMaxSeconds; const int64_t TimeUtil::kDurationMinSeconds; #endif // !_MSC_VER std::string TimeUtil::ToString(const Timestamp& timestamp) { return FormatTime(timestamp.seconds(), timestamp.nanos()); } bool TimeUtil::FromString(const std::string& value, Timestamp* timestamp) { int64_t seconds; int32_t nanos; if (!ParseTime(value, &seconds, &nanos)) { return false; } *timestamp = CreateNormalized(seconds, nanos); return true; } Timestamp TimeUtil::GetCurrentTime() { int64_t seconds; int32_t nanos; CurrentTime(&seconds, &nanos); return CreateNormalized(seconds, nanos); } Timestamp TimeUtil::GetEpoch() { return Timestamp(); } std::string TimeUtil::ToString(const Duration& duration) { std::string result; int64_t seconds = duration.seconds(); int32_t nanos = duration.nanos(); if (seconds < 0 || nanos < 0) { result += "-"; seconds = -seconds; nanos = -nanos; } result += StrCat(seconds); if (nanos != 0) { result += "." + FormatNanos(nanos); } result += "s"; return result; } static int64_t Pow(int64_t x, int y) { int64_t result = 1; for (int i = 0; i < y; ++i) { result *= x; } return result; } bool TimeUtil::FromString(const std::string& value, Duration* duration) { if (value.length() <= 1 || value[value.length() - 1] != 's') { return false; } bool negative = (value[0] == '-'); int sign_length = (negative ? 1 : 0); // Parse the duration value as two integers rather than a float value // to avoid precision loss. std::string seconds_part, nanos_part; size_t pos = value.find_last_of('.'); if (pos == std::string::npos) { seconds_part = value.substr(sign_length, value.length() - 1 - sign_length); nanos_part = "0"; } else { seconds_part = value.substr(sign_length, pos - sign_length); nanos_part = value.substr(pos + 1, value.length() - pos - 2); } char* end; int64_t seconds = strto64(seconds_part.c_str(), &end, 10); if (end != seconds_part.c_str() + seconds_part.length()) { return false; } int64_t nanos = strto64(nanos_part.c_str(), &end, 10); if (end != nanos_part.c_str() + nanos_part.length()) { return false; } nanos = nanos * Pow(10, 9 - nanos_part.length()); if (negative) { // If a Duration is negative, both seconds and nanos should be negative. seconds = -seconds; nanos = -nanos; } duration->set_seconds(seconds); duration->set_nanos(static_cast(nanos)); return true; } Duration TimeUtil::NanosecondsToDuration(int64_t nanos) { return CreateNormalized(nanos / kNanosPerSecond, nanos % kNanosPerSecond); } Duration TimeUtil::MicrosecondsToDuration(int64_t micros) { return CreateNormalized( micros / kMicrosPerSecond, (micros % kMicrosPerSecond) * kNanosPerMicrosecond); } Duration TimeUtil::MillisecondsToDuration(int64_t millis) { return CreateNormalized( millis / kMillisPerSecond, (millis % kMillisPerSecond) * kNanosPerMillisecond); } Duration TimeUtil::SecondsToDuration(int64_t seconds) { return CreateNormalized(seconds, 0); } Duration TimeUtil::MinutesToDuration(int64_t minutes) { return CreateNormalized(minutes * kSecondsPerMinute, 0); } Duration TimeUtil::HoursToDuration(int64_t hours) { return CreateNormalized(hours * kSecondsPerHour, 0); } int64_t TimeUtil::DurationToNanoseconds(const Duration& duration) { return duration.seconds() * kNanosPerSecond + duration.nanos(); } int64_t TimeUtil::DurationToMicroseconds(const Duration& duration) { return duration.seconds() * kMicrosPerSecond + RoundTowardZero(duration.nanos(), kNanosPerMicrosecond); } int64_t TimeUtil::DurationToMilliseconds(const Duration& duration) { return duration.seconds() * kMillisPerSecond + RoundTowardZero(duration.nanos(), kNanosPerMillisecond); } int64_t TimeUtil::DurationToSeconds(const Duration& duration) { return duration.seconds(); } int64_t TimeUtil::DurationToMinutes(const Duration& duration) { return RoundTowardZero(duration.seconds(), kSecondsPerMinute); } int64_t TimeUtil::DurationToHours(const Duration& duration) { return RoundTowardZero(duration.seconds(), kSecondsPerHour); } Timestamp TimeUtil::NanosecondsToTimestamp(int64_t nanos) { return CreateNormalized(nanos / kNanosPerSecond, nanos % kNanosPerSecond); } Timestamp TimeUtil::MicrosecondsToTimestamp(int64_t micros) { return CreateNormalized( micros / kMicrosPerSecond, micros % kMicrosPerSecond * kNanosPerMicrosecond); } Timestamp TimeUtil::MillisecondsToTimestamp(int64_t millis) { return CreateNormalized( millis / kMillisPerSecond, millis % kMillisPerSecond * kNanosPerMillisecond); } Timestamp TimeUtil::SecondsToTimestamp(int64_t seconds) { return CreateNormalized(seconds, 0); } int64_t TimeUtil::TimestampToNanoseconds(const Timestamp& timestamp) { return timestamp.seconds() * kNanosPerSecond + timestamp.nanos(); } int64_t TimeUtil::TimestampToMicroseconds(const Timestamp& timestamp) { return timestamp.seconds() * kMicrosPerSecond + RoundTowardZero(timestamp.nanos(), kNanosPerMicrosecond); } int64_t TimeUtil::TimestampToMilliseconds(const Timestamp& timestamp) { return timestamp.seconds() * kMillisPerSecond + RoundTowardZero(timestamp.nanos(), kNanosPerMillisecond); } int64_t TimeUtil::TimestampToSeconds(const Timestamp& timestamp) { return timestamp.seconds(); } Timestamp TimeUtil::TimeTToTimestamp(time_t value) { return CreateNormalized(static_cast(value), 0); } time_t TimeUtil::TimestampToTimeT(const Timestamp& value) { return static_cast(value.seconds()); } Timestamp TimeUtil::TimevalToTimestamp(const timeval& value) { return CreateNormalized(value.tv_sec, value.tv_usec * kNanosPerMicrosecond); } timeval TimeUtil::TimestampToTimeval(const Timestamp& value) { timeval result; result.tv_sec = value.seconds(); result.tv_usec = RoundTowardZero(value.nanos(), kNanosPerMicrosecond); return result; } Duration TimeUtil::TimevalToDuration(const timeval& value) { return CreateNormalized(value.tv_sec, value.tv_usec * kNanosPerMicrosecond); } timeval TimeUtil::DurationToTimeval(const Duration& value) { timeval result; result.tv_sec = value.seconds(); result.tv_usec = RoundTowardZero(value.nanos(), kNanosPerMicrosecond); // timeval.tv_usec's range is [0, 1000000) if (result.tv_usec < 0) { result.tv_sec -= 1; result.tv_usec += kMicrosPerSecond; } return result; } } // namespace util } // namespace protobuf } // namespace google namespace google { namespace protobuf { namespace { using ::PROTOBUF_NAMESPACE_ID::util::CreateNormalized; using ::PROTOBUF_NAMESPACE_ID::util::kNanosPerSecond; // Convert a Duration to uint128. void ToUint128(const Duration& value, uint128* result, bool* negative) { if (value.seconds() < 0 || value.nanos() < 0) { *negative = true; *result = static_cast(-value.seconds()); *result = *result * kNanosPerSecond + static_cast(-value.nanos()); } else { *negative = false; *result = static_cast(value.seconds()); *result = *result * kNanosPerSecond + static_cast(value.nanos()); } } void ToDuration(const uint128& value, bool negative, Duration* duration) { int64_t seconds = static_cast(Uint128Low64(value / kNanosPerSecond)); int32_t nanos = static_cast(Uint128Low64(value % kNanosPerSecond)); if (negative) { seconds = -seconds; nanos = -nanos; } duration->set_seconds(seconds); duration->set_nanos(nanos); } } // namespace Duration& operator+=(Duration& d1, const Duration& d2) { d1 = CreateNormalized(d1.seconds() + d2.seconds(), d1.nanos() + d2.nanos()); return d1; } Duration& operator-=(Duration& d1, const Duration& d2) { // NOLINT d1 = CreateNormalized(d1.seconds() - d2.seconds(), d1.nanos() - d2.nanos()); return d1; } Duration& operator*=(Duration& d, int64_t r) { // NOLINT bool negative; uint128 value; ToUint128(d, &value, &negative); if (r > 0) { value *= static_cast(r); } else { negative = !negative; value *= static_cast(-r); } ToDuration(value, negative, &d); return d; } Duration& operator*=(Duration& d, double r) { // NOLINT double result = (d.seconds() * 1.0 + 1.0 * d.nanos() / kNanosPerSecond) * r; int64_t seconds = static_cast(result); int32_t nanos = static_cast((result - seconds) * kNanosPerSecond); // Note that we normalize here not just because nanos can have a different // sign from seconds but also that nanos can be any arbitrary value when // overflow happens (i.e., the result is a much larger value than what // int64 can represent). d = CreateNormalized(seconds, nanos); return d; } Duration& operator/=(Duration& d, int64_t r) { // NOLINT bool negative; uint128 value; ToUint128(d, &value, &negative); if (r > 0) { value /= static_cast(r); } else { negative = !negative; value /= static_cast(-r); } ToDuration(value, negative, &d); return d; } Duration& operator/=(Duration& d, double r) { // NOLINT return d *= 1.0 / r; } Duration& operator%=(Duration& d1, const Duration& d2) { // NOLINT bool negative1, negative2; uint128 value1, value2; ToUint128(d1, &value1, &negative1); ToUint128(d2, &value2, &negative2); uint128 result = value1 % value2; // When negative values are involved in division, we round the division // result towards zero. With this semantics, sign of the remainder is the // same as the dividend. For example: // -5 / 10 = 0, -5 % 10 = -5 // -5 / (-10) = 0, -5 % (-10) = -5 // 5 / (-10) = 0, 5 % (-10) = 5 ToDuration(result, negative1, &d1); return d1; } int64_t operator/(const Duration& d1, const Duration& d2) { bool negative1, negative2; uint128 value1, value2; ToUint128(d1, &value1, &negative1); ToUint128(d2, &value2, &negative2); int64_t result = Uint128Low64(value1 / value2); if (negative1 != negative2) { result = -result; } return result; } Timestamp& operator+=(Timestamp& t, const Duration& d) { // NOLINT t = CreateNormalized(t.seconds() + d.seconds(), t.nanos() + d.nanos()); return t; } Timestamp& operator-=(Timestamp& t, const Duration& d) { // NOLINT t = CreateNormalized(t.seconds() - d.seconds(), t.nanos() - d.nanos()); return t; } Duration operator-(const Timestamp& t1, const Timestamp& t2) { return CreateNormalized(t1.seconds() - t2.seconds(), t1.nanos() - t2.nanos()); } } // namespace protobuf } // namespace google