| /* |
| * 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" |
| |
| namespace rocketmq { |
| |
| #ifdef WIN32 |
| int gettimeofdayWin(struct timeval* tp, void* tzp) { |
| time_t clock; |
| struct tm tm; |
| SYSTEMTIME wtm; |
| GetLocalTime(&wtm); |
| tm.tm_year = wtm.wYear - 1900; |
| tm.tm_mon = wtm.wMonth - 1; |
| tm.tm_mday = wtm.wDay; |
| tm.tm_hour = wtm.wHour; |
| tm.tm_min = wtm.wMinute; |
| tm.tm_sec = wtm.wSecond; |
| tm.tm_isdst = -1; |
| clock = mktime(&tm); |
| tp->tv_sec = clock; |
| tp->tv_usec = wtm.wMilliseconds * 1000; |
| return (0); |
| } |
| #endif |
| |
| StringIdMaker::StringIdMaker() { |
| memset(_buff, 0, sizeof(_buff)); |
| memset(_0x_buff, 0, sizeof(_0x_buff)); |
| srand((uint32_t)time(NULL)); |
| init_prefix(); |
| } |
| StringIdMaker::~StringIdMaker() {} |
| |
| void StringIdMaker::init_prefix() { |
| uint32_t pid = getpid(); |
| uint32_t ip = get_ip(); |
| uint32_t random_num = (rand() % 0xFFFF); |
| |
| memcpy(_buff + 2, &pid, 4); |
| memcpy(_buff, &ip, 4); |
| memcpy(_buff + 6, &random_num, 4); |
| |
| hexdump(_buff, _0x_buff, 10); |
| |
| set_start_and_next_tm(); |
| } |
| |
| uint32_t StringIdMaker::get_ip() { |
| char name[1024]; |
| boost::system::error_code ec; |
| if (boost::asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) { |
| return 0; |
| } |
| |
| boost::asio::io_service io_service; |
| boost::asio::ip::tcp::resolver resolver(io_service); |
| boost::asio::ip::tcp::resolver::query query(name, ""); |
| boost::system::error_code error; |
| boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query, error); |
| if (error) { |
| return 0; |
| } |
| boost::asio::ip::tcp::resolver::iterator end; // End marker. |
| boost::asio::ip::tcp::endpoint ep; |
| while (iter != end) { |
| ep = *iter++; |
| } |
| std::string s_localIpAddress = ep.address().to_string(); |
| |
| int a[4]; |
| std::string IP = s_localIpAddress; |
| std::string strTemp; |
| size_t pos; |
| size_t i = 3; |
| |
| do { |
| pos = IP.find("."); |
| |
| if (pos != std::string::npos) { |
| strTemp = IP.substr(0, pos); |
| a[i] = atoi(strTemp.c_str()); |
| i--; |
| IP.erase(0, pos + 1); |
| } else { |
| strTemp = IP; |
| a[i] = atoi(strTemp.c_str()); |
| break; |
| } |
| |
| } while (1); |
| |
| uint32_t nResult = (a[3] << 24) + (a[2] << 16) + (a[1] << 8) + a[0]; |
| return nResult; |
| } |
| |
| uint64_t StringIdMaker::get_curr_ms() { |
| struct timeval time_now; |
| // windows and linux use the same function name, windows's defination as begining this file |
| #ifdef WIN32 |
| gettimeofdayWin(&time_now, NULL); // WIN32 |
| #else |
| gettimeofday(&time_now, NULL); // LINUX |
| #endif |
| |
| uint64_t ms_time = time_now.tv_sec * 1000 + time_now.tv_usec / 1000; |
| return ms_time; |
| } |
| |
| void StringIdMaker::set_start_and_next_tm() { |
| time_t tmNow = time(NULL); |
| tm* ptmNow = localtime(&tmNow); |
| tm mon_begin; |
| mon_begin.tm_year = ptmNow->tm_year; |
| mon_begin.tm_mon = ptmNow->tm_mon; |
| mon_begin.tm_mday = 0; |
| mon_begin.tm_hour = 0; |
| mon_begin.tm_min = 0; |
| mon_begin.tm_sec = 0; |
| |
| tm mon_next_begin; |
| if (ptmNow->tm_mon == 12) { |
| mon_next_begin.tm_year = ptmNow->tm_year + 1; |
| mon_next_begin.tm_mon = 1; |
| } else { |
| mon_next_begin.tm_year = ptmNow->tm_year; |
| mon_next_begin.tm_mon = ptmNow->tm_mon + 1; |
| } |
| mon_next_begin.tm_mday = 0; |
| mon_next_begin.tm_hour = 0; |
| mon_next_begin.tm_min = 0; |
| mon_next_begin.tm_sec = 0; |
| |
| time_t mon_begin_tm = mktime(&mon_begin); |
| time_t mon_end_tm = mktime(&mon_next_begin); |
| |
| _start_tm = mon_begin_tm * 1000; |
| _next_start_tm = mon_end_tm * 1000; |
| } |
| |
| int StringIdMaker::atomic_incr(int id) { |
| #ifdef WIN32 |
| InterlockedIncrement((LONG*)&id); |
| #else |
| __sync_add_and_fetch(&id, 1); |
| #endif |
| return id; |
| } |
| std::string StringIdMaker::get_unique_id() { |
| uint64_t now_time = get_curr_ms(); |
| |
| if (now_time > _next_start_tm) { |
| set_start_and_next_tm(); |
| } |
| uint32_t tm_period = now_time - _start_tm; |
| seqid = atomic_incr(seqid) & 0xFF; |
| |
| std::size_t prifix_len = 10; // 10 = prefix len |
| unsigned char* write_index = _buff + prifix_len; |
| |
| memcpy(write_index, &tm_period, 4); |
| write_index += 4; |
| |
| memcpy(write_index, &seqid, 2); |
| |
| hexdump(_buff + prifix_len, (_0x_buff + (2 * prifix_len)), 6); |
| _0x_buff[32] = '\0'; |
| return std::string(_0x_buff); |
| } |
| |
| void StringIdMaker::hexdump(unsigned char* buffer, char* out_buff, unsigned long index) { |
| for (unsigned long i = 0; i < index; i++) { |
| sprintf(out_buff + 2 * i, "%02X ", buffer[i]); |
| } |
| } |
| } |