| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| #include "paimon/common/options/time_duration.h" | |
| #include <cstddef> | |
| #include <limits> | |
| #include <optional> | |
| #include <utility> | |
| #include "fmt/format.h" | |
| #include "paimon/common/utils/string_utils.h" | |
| #include "paimon/result.h" | |
| #include "paimon/status.h" | |
| namespace paimon { | |
| const TimeDuration::TimeUnit& TimeDuration::NanoSeconds() { | |
| static const TimeUnit kNanoSeconds{{"ns", "nano", "nanos", "nanosecond", "nanoseconds"}, | |
| 1000 * 1000L, | |
| TimeUnit::CoefficientOperator::DIVIDE}; | |
| return kNanoSeconds; | |
| } | |
| const TimeDuration::TimeUnit& TimeDuration::MicroSeconds() { | |
| static const TimeUnit kMicroSeconds{ | |
| {"us", "µs", "micro", "micros", "microsecond", "microseconds"}, | |
| 1000L, | |
| TimeUnit::CoefficientOperator::DIVIDE}; | |
| return kMicroSeconds; | |
| } | |
| const TimeDuration::TimeUnit& TimeDuration::MilliSeconds() { | |
| static const TimeUnit kMilliSeconds{{"ms", "milli", "millis", "millisecond", "milliseconds"}, | |
| 1, | |
| TimeUnit::CoefficientOperator::MULTIPLY}; | |
| return kMilliSeconds; | |
| } | |
| const TimeDuration::TimeUnit& TimeDuration::Seconds() { | |
| static const TimeUnit kSeconds{ | |
| {"s", "sec", "secs", "second", "seconds"}, 1000L, TimeUnit::CoefficientOperator::MULTIPLY}; | |
| return kSeconds; | |
| } | |
| const TimeDuration::TimeUnit& TimeDuration::Minutes() { | |
| static const TimeUnit kMinutes{ | |
| {"min", "m", "minute", "minutes"}, 1000L * 60, TimeUnit::CoefficientOperator::MULTIPLY}; | |
| return kMinutes; | |
| } | |
| const TimeDuration::TimeUnit& TimeDuration::Hours() { | |
| static const TimeUnit kHours{ | |
| {"h", "hour", "hours"}, 1000L * 60 * 60, TimeUnit::CoefficientOperator::MULTIPLY}; | |
| return kHours; | |
| } | |
| const TimeDuration::TimeUnit& TimeDuration::Days() { | |
| static const TimeUnit kDays{ | |
| {"d", "day", "days"}, 1000L * 60 * 60 * 24, TimeUnit::CoefficientOperator::MULTIPLY}; | |
| return kDays; | |
| } | |
| Result<int64_t> TimeDuration::Parse(const std::string& text) { | |
| std::string trimmed = text; | |
| StringUtils::Trim(&trimmed); | |
| if (trimmed.empty()) { | |
| return Status::Invalid("argument is an empty or whitespace-only string"); | |
| } | |
| const size_t len = trimmed.length(); | |
| size_t pos = 0; | |
| char current; | |
| while (pos < len) { | |
| current = trimmed[pos]; | |
| if (current >= '0' && current <= '9') { | |
| pos++; | |
| } else { | |
| break; | |
| } | |
| } | |
| std::string number = trimmed.substr(0, pos); | |
| if (number.empty()) { | |
| return Status::Invalid("text does not start with a number"); | |
| } | |
| std::string raw_unit = trimmed.substr(pos); | |
| StringUtils::Trim(&raw_unit); | |
| std::string unit = StringUtils::ToLowerCase(raw_unit); | |
| std::optional<int64_t> value = StringUtils::StringToValue<int64_t>(number); | |
| if (value == std::nullopt) { | |
| return Status::Invalid(fmt::format( | |
| "The value '{}' cannot be represented as 64bit number (numeric overflow).", number)); | |
| } | |
| PAIMON_ASSIGN_OR_RAISE(TimeUnit parsed_unit, ParseUnit(unit)); | |
| int64_t coefficient = parsed_unit.GetCoefficient(); | |
| TimeUnit::CoefficientOperator op = parsed_unit.GetCoefficientOperator(); | |
| int64_t maximum = std::numeric_limits<int64_t>::max() / | |
| (op == TimeUnit::CoefficientOperator::DIVIDE ? 1 : coefficient); | |
| // check for overflow | |
| if (value.value() > maximum) { | |
| return Status::Invalid( | |
| fmt::format("The value '{}' cannot be represented as 64bit number of milliseconds " | |
| "(numeric overflow).", | |
| text)); | |
| } | |
| if (op == TimeUnit::CoefficientOperator::MULTIPLY) { | |
| return value.value() * coefficient; | |
| } else { | |
| return value.value() / coefficient; | |
| } | |
| } | |
| Result<TimeDuration::TimeUnit> TimeDuration::ParseUnit(const std::string& unit) { | |
| if (MatchesAny(unit, NanoSeconds())) { | |
| return NanoSeconds(); | |
| } else if (MatchesAny(unit, MicroSeconds())) { | |
| return MicroSeconds(); | |
| } else if (MatchesAny(unit, MilliSeconds())) { | |
| return MilliSeconds(); | |
| } else if (MatchesAny(unit, Seconds())) { | |
| return Seconds(); | |
| } else if (MatchesAny(unit, Minutes())) { | |
| return Minutes(); | |
| } else if (MatchesAny(unit, Hours())) { | |
| return Hours(); | |
| } else if (MatchesAny(unit, Days())) { | |
| return Days(); | |
| } else if (!unit.empty()) { | |
| return Status::Invalid(fmt::format( | |
| "Time duration unit '{}' does not match any of the recognized units", unit)); | |
| } | |
| return MilliSeconds(); | |
| } | |
| bool TimeDuration::MatchesAny(const std::string& str, const TimeDuration::TimeUnit& unit) { | |
| for (const auto& u : unit.GetUnits()) { | |
| if (u == str) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| } // namespace paimon |