remove boost from StringIdMaker, and fix some bugs. (#183)

diff --git a/src/message/BatchMessage.cpp b/src/message/BatchMessage.cpp
index 89662bc..3a2481b 100644
--- a/src/message/BatchMessage.cpp
+++ b/src/message/BatchMessage.cpp
@@ -14,18 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include "BatchMessage.h"
+
 #include "MQDecoder.h"
 #include "StringIdMaker.h"
 
-using namespace std;
 namespace rocketmq {
 
 std::string BatchMessage::encode(std::vector<MQMessage>& msgs) {
-  string encodedBody;
+  std::string encodedBody;
   for (auto message : msgs) {
-    string unique_id = StringIdMaker::get_mutable_instance().get_unique_id();
+    std::string unique_id = StringIdMaker::getInstance().createUniqID();
     message.setProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, unique_id);
     encodedBody.append(encode(message));
   }
@@ -59,4 +58,5 @@
   encodeMsg.append(properties.c_str(), propertiesLength);
   return encodeMsg;
 }
-}
+
+}  // namespace rocketmq
diff --git a/src/producer/DefaultMQProducer.cpp b/src/producer/DefaultMQProducer.cpp
index 557f529..980a373 100644
--- a/src/producer/DefaultMQProducer.cpp
+++ b/src/producer/DefaultMQProducer.cpp
@@ -395,7 +395,7 @@
       bool isBatchMsg = std::type_index(typeid(msg)) == std::type_index(typeid(BatchMessage));
       // msgId is produced by client, offsetMsgId produced by broker. (same with java sdk)
       if (!isBatchMsg) {
-        string unique_id = StringIdMaker::get_mutable_instance().get_unique_id();
+        string unique_id = StringIdMaker::getInstance().createUniqID();
         msg.setProperty(MQMessage::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, unique_id);
 
         // batch does not support compressing right now,
diff --git a/src/producer/StringIdMaker.cpp b/src/producer/StringIdMaker.cpp
index 1b83fd8..13978e3 100644
--- a/src/producer/StringIdMaker.cpp
+++ b/src/producer/StringIdMaker.cpp
@@ -16,175 +16,121 @@
  */
 #include "StringIdMaker.h"
 
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+
+#include "ByteOrder.h"
+#include "UtilAll.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
+const char StringIdMaker::sHexAlphabet[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                                              '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
 
 StringIdMaker::StringIdMaker() {
-  memset(_buff, 0, sizeof(_buff));
-  memset(_0x_buff, 0, sizeof(_0x_buff));
-  srand((uint32_t)time(NULL));
-  init_prefix();
+  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() {}
 
-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) {
+uint32_t StringIdMaker::getIP() {
+  std::string ip = UtilAll::getLocalAddress();
+  if (ip.empty()) {
     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;
+  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--);
   }
-  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;
+  delete[] ip_str;
 
-  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
+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
 
-  uint64_t ms_time = time_now.tv_sec * 1000 + time_now.tv_usec / 1000;
-  return ms_time;
-}
+  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]
 
-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;
+  std::tm nextMonthBegin = {0};
+  if (ptmNow->tm_mon >= 11) {
+    nextMonthBegin.tm_year = ptmNow->tm_year + 1;
+    nextMonthBegin.tm_mon = 0;
   } else {
-    mon_next_begin.tm_year = ptmNow->tm_year;
-    mon_next_begin.tm_mon = ptmNow->tm_mon + 1;
+    nextMonthBegin.tm_year = ptmNow->tm_year;
+    nextMonthBegin.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;
+  nextMonthBegin.tm_mday = 1;
+  nextMonthBegin.tm_hour = 0;
+  nextMonthBegin.tm_min = 0;
+  nextMonthBegin.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;
+  mStartTime = std::mktime(&curMonthBegin) * 1000;
+  mNextStartTime = std::mktime(&nextMonthBegin) * 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();
+std::string StringIdMaker::createUniqID() {
+  uint64_t current = UtilAll::currentTimeMillis();
+  if (current >= mNextStartTime) {
+    setStartTime(current);
+    current = UtilAll::currentTimeMillis();
   }
-  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;
+  uint32_t period = ByteOrder::swapIfLittleEndian(static_cast<uint32_t>(current - mStartTime));
+  uint16_t seqid = ByteOrder::swapIfLittleEndian(mCounter++);
 
-  memcpy(write_index, &tm_period, 4);
-  write_index += 4;
+  unsigned char bin_buf[6];
+  std::memcpy(bin_buf, &period, 4);
+  std::memcpy(bin_buf + 4, &seqid, 2);
 
-  memcpy(write_index, &seqid, 2);
+  char hex_buf[12];
+  hexdump(bin_buf, hex_buf, 6);
 
-  hexdump(_buff + prifix_len, (_0x_buff + (2 * prifix_len)), 6);
-  _0x_buff[32] = '\0';
-  return std::string(_0x_buff);
+  return std::string(kFixString, 20) + std::string(hex_buf, 12);
 }
 
-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]);
+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
diff --git a/src/producer/StringIdMaker.h b/src/producer/StringIdMaker.h
index b693787..872bf05 100644
--- a/src/producer/StringIdMaker.h
+++ b/src/producer/StringIdMaker.h
@@ -14,61 +14,51 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-/*
-        ip: 4
-        pid: 4
-        随机数 :2
-        时间:4
-        自增数:2
-*/
 #ifndef __STRINGID_MAKER_H__
 #define __STRINGID_MAKER_H__
 
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <boost/serialization/singleton.hpp>
+#include <atomic>
+#include <cstdint>
 #include <string>
-#include <boost/asio.hpp>
-
-#ifdef WIN32
-#include <windows.h>
-#else
-#include <unistd.h>
-#include <sys/time.h>
-#endif
-
-#ifdef WIN32
-#include <windows.h>
-#else
-#include <unistd.h>
-#include <sys/time.h>
-#endif
 
 namespace rocketmq {
-class StringIdMaker : public boost::serialization::singleton<StringIdMaker> {
- public:
+
+class StringIdMaker {
+ private:
   StringIdMaker();
   ~StringIdMaker();
-  std::string get_unique_id();
+
+ public:
+  static StringIdMaker& getInstance() {
+    // After c++11, the initialization occurs exactly once
+    static StringIdMaker singleton_;
+    return singleton_;
+  }
+
+  /* ID format:
+   *   ip: 4 bytes
+   *   pid: 2 bytes
+   *   random: 4 bytes
+   *   time: 4 bytes
+   *   auto num: 2 bytes
+   */
+  std::string createUniqID();
 
  private:
-  uint32_t get_ip();
-  void init_prefix();
-  uint64_t get_curr_ms();
-  int atomic_incr(int id);
-  void set_start_and_next_tm();
+  void setStartTime(uint64_t millis);
 
-  void hexdump(unsigned char* buffer, char* out_buff, unsigned long index);
+  static uint32_t getIP();
+  static void hexdump(unsigned char* buffer, char* out_buff, unsigned long index);
 
  private:
-  uint64_t _start_tm;
-  uint64_t _next_start_tm;
-  unsigned char _buff[16];
-  char _0x_buff[33];
-  int16_t seqid;
+  uint64_t mStartTime;
+  uint64_t mNextStartTime;
+  std::atomic<uint16_t> mCounter;
+
+  char kFixString[21];
+
+  static const char sHexAlphabet[16];
 };
-}
+
+}  // namespace rocketmq
 #endif
diff --git a/test/src/StringIdMakerTest.cpp b/test/src/StringIdMakerTest.cpp
index 4fa25d3..ebe5897 100644
--- a/test/src/StringIdMakerTest.cpp
+++ b/test/src/StringIdMakerTest.cpp
@@ -14,20 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "gtest/gtest.h"
-#include "gmock/gmock.h"
-#include "StringIdMaker.h"
-#include <map>
 #include <iostream>
+#include <map>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "StringIdMaker.h"
 
 using namespace std;
 using namespace rocketmq;
-using ::testing::InitGoogleTest;
 using ::testing::InitGoogleMock;
+using ::testing::InitGoogleTest;
 using testing::Return;
 
 TEST(StringIdMakerTest, get_unique_id) {
-  string unique_id = StringIdMaker::get_mutable_instance().get_unique_id();
+  string unique_id = StringIdMaker::getInstance().createUniqID();
   cout << "unique_id: " << unique_id << endl;
   EXPECT_EQ(unique_id.size(), 32);
 }