| /* |
| * 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 "StringIdMaker.h" |
| |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <ctime> |
| |
| #include "ByteOrder.h" |
| #include "UtilAll.h" |
| |
| namespace rocketmq { |
| |
| const char StringIdMaker::sHexAlphabet[16] = {'0', '1', '2', '3', '4', '5', '6', '7', |
| '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; |
| |
| StringIdMaker::StringIdMaker() { |
| std::srand((uint32_t)std::time(NULL)); |
| |
| uint32_t pid = ByteOrder::swapIfLittleEndian(static_cast<uint32_t>(getpid())); |
| uint32_t ip = ByteOrder::swapIfLittleEndian(getIP()); |
| uint32_t random_num = ByteOrder::swapIfLittleEndian(static_cast<uint32_t>(std::rand())); |
| |
| unsigned char bin_buf[10]; |
| std::memcpy(bin_buf + 2, &pid, 4); |
| std::memcpy(bin_buf, &ip, 4); |
| std::memcpy(bin_buf + 6, &random_num, 4); |
| |
| hexdump(bin_buf, kFixString, 10); |
| kFixString[20] = '\0'; |
| |
| setStartTime(UtilAll::currentTimeMillis()); |
| |
| mCounter = 0; |
| } |
| |
| StringIdMaker::~StringIdMaker() {} |
| |
| uint32_t StringIdMaker::getIP() { |
| std::string ip = UtilAll::getLocalAddress(); |
| if (ip.empty()) { |
| return 0; |
| } |
| |
| char* ip_str = new char[ip.length() + 1]; |
| std::strncpy(ip_str, ip.c_str(), ip.length()); |
| ip_str[ip.length()] = '\0'; |
| |
| int i = 3; |
| uint32_t nResult = 0; |
| for (char* token = std::strtok(ip_str, "."); token != nullptr && i >= 0; token = std::strtok(nullptr, ".")) { |
| uint32_t n = std::atoi(token); |
| nResult |= n << (8 * i--); |
| } |
| |
| delete[] ip_str; |
| |
| return nResult; |
| } |
| |
| void StringIdMaker::setStartTime(uint64_t millis) { |
| // std::time_t |
| // Although not defined, this is almost always an integral value holding the number of seconds |
| // (not counting leap seconds) since 00:00, Jan 1 1970 UTC, corresponding to POSIX time. |
| std::time_t tmNow = millis / 1000; |
| std::tm* ptmNow = std::localtime(&tmNow); // may not be thread-safe |
| |
| std::tm curMonthBegin = {0}; |
| curMonthBegin.tm_year = ptmNow->tm_year; // since 1900 |
| curMonthBegin.tm_mon = ptmNow->tm_mon; // [0, 11] |
| curMonthBegin.tm_mday = 1; // [1, 31] |
| curMonthBegin.tm_hour = 0; // [0, 23] |
| curMonthBegin.tm_min = 0; // [0, 59] |
| curMonthBegin.tm_sec = 0; // [0, 60] |
| |
| std::tm nextMonthBegin = {0}; |
| if (ptmNow->tm_mon >= 11) { |
| nextMonthBegin.tm_year = ptmNow->tm_year + 1; |
| nextMonthBegin.tm_mon = 0; |
| } else { |
| nextMonthBegin.tm_year = ptmNow->tm_year; |
| nextMonthBegin.tm_mon = ptmNow->tm_mon + 1; |
| } |
| nextMonthBegin.tm_mday = 1; |
| nextMonthBegin.tm_hour = 0; |
| nextMonthBegin.tm_min = 0; |
| nextMonthBegin.tm_sec = 0; |
| |
| mStartTime = std::mktime(&curMonthBegin) * 1000; |
| mNextStartTime = std::mktime(&nextMonthBegin) * 1000; |
| } |
| |
| std::string StringIdMaker::createUniqID() { |
| uint64_t current = UtilAll::currentTimeMillis(); |
| if (current >= mNextStartTime) { |
| setStartTime(current); |
| current = UtilAll::currentTimeMillis(); |
| } |
| |
| uint32_t period = ByteOrder::swapIfLittleEndian(static_cast<uint32_t>(current - mStartTime)); |
| uint16_t seqid = ByteOrder::swapIfLittleEndian(mCounter++); |
| |
| unsigned char bin_buf[6]; |
| std::memcpy(bin_buf, &period, 4); |
| std::memcpy(bin_buf + 4, &seqid, 2); |
| |
| char hex_buf[12]; |
| hexdump(bin_buf, hex_buf, 6); |
| |
| return std::string(kFixString, 20) + std::string(hex_buf, 12); |
| } |
| |
| void StringIdMaker::hexdump(unsigned char* in, char* out, std::size_t len) { |
| for (std::size_t i = 0; i < len; i++) { |
| unsigned char v = in[i]; |
| out[i * 2] = sHexAlphabet[v >> 4]; |
| out[i * 2 + 1] = sHexAlphabet[v & 0x0FU]; |
| } |
| } |
| |
| } // namespace rocketmq |