blob: 13978e3f5d042c9e623423457016e951ca865837 [file] [log] [blame]
/*
* 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