一个UUID生成算法的C语言实现——WIN32版本


 


cheungmine


2007-9-16


 


根据定义,UUID(Universally Unique IDentifier,也称GUID)在时间和空间都是唯一的。为保证空间的唯一性,每个UUID使用了一个48位的值来记录,一般是计算机的网卡地址。为保证时间上的唯一性,每个UUID具有一个60位的时间戳(timestamp)。这个时间戳表示自公元1582年(绝对不是1852,这是《COM技术内幕》,1999年3月第1版第89页中的一个错误)10月15号00:00:00:00以来的时间,是以100纳秒为单位的时间间隔。1纳秒(ns)=10 -9秒(s)。UUID算法可以保证至大约公元3400年仍然唯一。UUID的C语言结构定义如下:


typedef
 struct
 
{
 
unsigned long
 
unsigned short
 
unsigned short
 
unsigned char
 
} uuid_t;


 


它的结构大小为16个字节。即sizeof(uuid_t)==16为TRUE。写成16进制字符串的格式,一般为:


"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"


 


上面的字符串形式,占用36个字符,不包括结尾空字符’/0’。所以,要想容纳一个UUID字符串,必须声明为一个char[36+1]的字符数组。


 


以软件算法实现UUID非常有现实意义。参考RFC4122文档和其他一些开源代码,我写了一个WIN32下的UUID实现C语言程序——UUID32.c。程序符合RFC4122标准。程序不但实现创建UUID和UUID String,还可以对UUID进行字符和时间上的比较。还可以从UUID从提取时间戳(精度到秒)。头文件uuid32.h定义如下:

/*
    uuid32.h 
   2007-09-15 Last created by cheungmine.
   Partly rights reserved by cheungmine.

   */
   
#ifndef UUID32_H_INCLUDED

   #define
    UUID32_H_INCLUDED
   

#include 
   <
   stdlib.h
   >
   
#include 
   <
   assert.h
   >
   
#include 
   <
   string
   .h
   >
   
#include 
   <
   memory.h
   >
   

#include 
   "
   cdatatype.h
   "
   

typedef 
   struct
    _timestamp_t
{
    BYTE    tm_sec;                
   /*
    Seconds after minute (0 – 59). 
   */
   
    BYTE    tm_min;                
   /*
    Minutes after hour (0 – 59). 
   */
   
    BYTE    tm_hour;            
   /*
    Hours after midnight (0 – 23). 
   */
   
    BYTE    tm_mday;            
   /*
    Day of month (1 – 31). 
   */
   
    BYTE    tm_mon;                
   /*
    Month (0 – 11; January = 0). 
   */
   
    BYTE    tm_wday;            
   /*
    Day of week (0 – 6; Sunday = 0). 
   */
   
    
   short
       tm_year;            
   /*
    Year (current year minus 1900). 
   */
   
    
   short
       tm_yday;            
   /*
    Day of year (0 – 365; January 1 = 0). 
   */
   
    
   long
       tm_fraction;        
   /*
    Fraction little than 1 second 
   */
   
} timestamp_t;

typedef 
   struct
    _uuid_t
{
    unsigned 
   long
       data1;
    unsigned 
   short
       data2;
    unsigned 
   short
       data3;
    unsigned 
   char
       data4[
   8
   ];
} uuid_t;



   /*
   *
 * Checks whether the given string matches the UUID format.
 *    params:
 *     [in] uuid - the potential UUID string
 *    return 
 *     TRUE if the given string is a UUID, FALSE otherwise
 *
   */
   
BOOL is_uuid_string(
   const
    
   char
    
   *
   uuid);


   /*
   *
 * Generates a new UUID. The UUID is a time-based time 1 UUID.
 * A random per-process node identifier is used to avoid keeping global
 * state and maintaining inter-process synchronization.
 *
   */
   

   void
    uuid_create(uuid_t
   *
    uuid);


   /*
   *
 * Generates a new UUID string. The returned UUID is a time-based time 1 UUID.
 * A random per-process node identifier is used to avoid keeping global
 * state and maintaining inter-process synchronization.
 *  return UUID string (newly allocated)
 *
   */
   

   char
    
   *
   uuid_create_string(
   void
   );


   /*
   *
 * Generates a name-based (type 3) UUID string from the given external
 * identifier. The special namespace UUID is used as the namespace of
 * the generated UUID.
 *  params
 *     [in] external - the external identifier
 *  return 
 *     UUID string (newly allocated)
 *
   */
   

   void
    uuid_create_external(
   const
    
   char
    
   *
   external, uuid_t
   *
    uuid);


   /*
   *
 * Translate a uuid_t to a uuid string
 *  return UUID string
 *
   */
   

   char
    
   *
   uuid_to_string(
   const
    uuid_t
   *
    uuid);


   /*
   *
 * Get timestamp from a UUID
 *
   */
   

   void
    uuid_to_timestamp(
   const
    uuid_t
   *
    uuid, timestamp_t
   *
    time);



   /*
   *
 * Resurn a description of timestamp NOT including fraction
 *
   */
   

   char
   *
    timestamp_to_string(
   const
    timestamp_t
   *
    time);


   /*
   *
 * Compare two UUID's lexically
 *    return
 *      -1   u1 is lexically before u2
 *     0   u1 is equal to u2
 *     1   u1 is lexically after u2

   */
   

   int
    uuid_compare(
   const
    uuid_t 
   *
   u1, 
   const
    uuid_t 
   *
   u2);


   /*
   *
 * Compare two UUID's temporally
 *    return
 *      -1   u1 is temporally before u2
 *     0   u1 is equal to u2
 *     1   u1 is temporally after u2

   */
   

   int
    uuid_compare_time(
   const
    uuid_t 
   *
   u1, 
   const
    uuid_t 
   *
   u2);



   #endif
           /* UUID32_H_INCLUDED */

  


       其中,头文件"cdatatype.h"如下:


/*
    cdatatype.h 
   2008-09-15 Last created by cheungmine.
   All rights reserved by cheungmine.

   */
   
#ifndef CDATATYPE_H__

   #define
    CDATATYPE_H__
   


   /*
   ============================================================================
   */
   
typedef unsigned 
   char
    uchar, 
   byte
   , BYTE;

typedef unsigned 
   short
    uint16, word_t, 
   ushort
   ;

typedef unsigned 
   int
    
   uint
   , uint32, dword_t, size_t;

typedef unsigned 
   long
    
   ulong
   ;

typedef __int64 int64;
typedef unsigned __int64 uint64, qword_t;


#ifndef BOOL
    
   #define
    BOOL  int
   
    
   #define
    TRUE  1
   
    
   #define
    FALSE 0
   

   #endif
   

#ifndef RESULT
    
   #define
    RESULT  long
   
    
   #define
    SUCCESS        0
   
    
   #define
    ERROR        -1
   

   #endif
   


   #define
    SIZE_BYTE    1
   

   #define
    SIZE_SHORT    2
   

   #define
    SIZE_INT    4
   

   #define
    SIZE_FLT    4
   

   #define
    SIZE_DBL    8
   

   #define
    SIZE_WORD    2
   

   #define
    SIZE_DWORD    4
   

   #define
    SIZE_QWORD    8
   

   #define
    SIZE_LINT    8
   

   #define
    SIZE_INT64    8
   

   #define
    SIZE_UUID    16
   


   /*
   ============================================================================
   */
   

   #endif
       /*CDATATYPE_H__*/


           MD5算法生成的文件有:md5.h和md5.c,分别罗列如下:

 

#ifndef _MD5_H__

  #define
   _MD5_H__
  

  /*
   MD5.H - header file for MD5C.C 
  */
  


  /*
   Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
   rights reserved.

   License to copy and use this software is granted provided that it
   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
   Algorithm" in all material mentioning or referencing this software
   or this function.

   License is also granted to make and use derivative works provided
   that such works are identified as "derived from the RSA Data
   Security, Inc. MD5 Message-Digest Algorithm" in all material
   mentioning or referencing the derived work.

   RSA Data Security, Inc. makes no representations concerning either
   the merchantability of this software or the suitability of this
   software for any particular purpose. It is provided "as is"
   without express or implied warranty of any kind.

   These notices must be retained in any copies of any part of this
   documentation and/or software.

   2007-09-15 Last modified by cheungmine.
 
  */
  


  /*
   MD5 context. 
  */
  
typedef 
  struct
   {
    unsigned 
  int
   state[
  4
  ];                
  /*
   state (ABCD) 
  */
  
    unsigned 
  int
   count[
  2
  ];                
  /*
   number of bits, modulo 2^64 (lsb first) 
  */
  
    unsigned 
  char
   buffer[
  64
  ];    
  /*
   input buffer 
  */
  
} MD5_CTX;


  void
    MD5_init (MD5_CTX 
  *
  );

  void
    MD5_update (MD5_CTX 
  *
  , 
  const
   unsigned 
  char
   
  *
  str, unsigned 
  int
   len);

  void
    MD5_fini (unsigned 
  char
  [
  16
  ], MD5_CTX 
  *
  );


  char
  *
   MD5_sign (
  const
   unsigned 
  char
   
  *
  str, unsigned 
  int
   len);


  #endif
      /* _MD5_H__ */
   
 
 
/*
   
 * md5.c - Copyright 1997 Lachlan Roche 
 *       - Modified by cheungmine, 2007-9-15
 
  */
  

  
#include 
  <
  stdio.h
  >
  
#include 
  <
  stdlib.h
  >
  
#include 
  <
  string
  .h
  >
  
#include 
  <
  memory.h
  >
  
#include 
  "
  md5.h
  "
  


  #define
   MD5STR_LEN        32    
  



  /*
  =====================================================================
   The remaining code is the reference MD5 code (md5c.c) from rfc1321
   MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm

   Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
   rights reserved.

   License to copy and use this software is granted provided that it
   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
   Algorithm" in all material mentioning or referencing this software
   or this function.

   License is also granted to make and use derivative works provided
   that such works are identified as "derived from the RSA Data
   Security, Inc. MD5 Message-Digest Algorithm" in all material
   mentioning or referencing the derived work.

   RSA Data Security, Inc. makes no representations concerning either
   the merchantability of this software or the suitability of this
   software for any particular purpose. It is provided "as is"
   without express or implied warranty of any kind.

   These notices must be retained in any copies of any part of this
   documentation and/or software.
=====================================================================
  */
  


  /*
   Constants for _MD5Transform routine. 
  */
  

  #define
   S11 7
  

  #define
   S12 12
  

  #define
   S13 17
  

  #define
   S14 22
  

  #define
   S21 5
  

  #define
   S22 9
  

  #define
   S23 14
  

  #define
   S24 20
  

  #define
   S31 4
  

  #define
   S32 11
  

  #define
   S33 16
  

  #define
   S34 23
  

  #define
   S41 6
  

  #define
   S42 10
  

  #define
   S43 15
  

  #define
   S44 21
  


  static
   
  void
   _MD5Transform(unsigned 
  int
  [
  4
  ], 
  const
   unsigned 
  char
  [
  64
  ]);

  static
   
  void
   _Encode(unsigned 
  char
   
  *
  , unsigned 
  int
   
  *
  , unsigned 
  int
  );

  static
   
  void
   _Decode(unsigned 
  int
   
  *
  , 
  const
   unsigned 
  char
   
  *
  , unsigned 
  int
  );


  static
   unsigned 
  char
   PADDING[
  64
  ] 
  =
   {
    
  0x80
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  ,
    
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  ,
    
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  , 
  0
  
};


  /*
   F, G, H and I are basic MD5 functions. 
  */
  

  #define
   F(x, y, z) (((x) & (y)) | ((~x) & (z)))
  

  #define
   G(x, y, z) (((x) & (z)) | ((y) & (~z)))
  

  #define
   H(x, y, z) ((x) ^ (y) ^ (z))
  

  #define
   I(x, y, z) ((y) ^ ((x) | (~z)))
  


  /*
   ROTATE_LEFT rotates x left n bits. 
  */
  

  #define
   ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
  


  /*
   FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
   Rotation is separate from addition to prevent recomputation. 
  */
  

  #define
   FF(a, b, c, d, x, s, ac) { 
  
 (a) 
  +=
   F ((b), (c), (d)) 
  +
   (x) 
  +
   (unsigned 
  int
  )(ac); 
 (a) 
  =
   ROTATE_LEFT ((a), (s)); 
 (a) 
  +=
   (b); 
  }

  #define
   GG(a, b, c, d, x, s, ac) { 
  
 (a) 
  +=
   G ((b), (c), (d)) 
  +
   (x) 
  +
   (unsigned 
  int
  )(ac); 
 (a) 
  =
   ROTATE_LEFT ((a), (s)); 
 (a) 
  +=
   (b); 
  }

  #define
   HH(a, b, c, d, x, s, ac) { 
  
 (a) 
  +=
   H ((b), (c), (d)) 
  +
   (x) 
  +
   (unsigned 
  int
  )(ac); 
 (a) 
  =
   ROTATE_LEFT ((a), (s)); 
 (a) 
  +=
   (b); 
  }

  #define
   II(a, b, c, d, x, s, ac) { 
  
 (a) 
  +=
   I ((b), (c), (d)) 
  +
   (x) 
  +
   (unsigned 
  int
  )(ac); 
 (a) 
  =
   ROTATE_LEFT ((a), (s)); 
 (a) 
  +=
   (b); 
  }


  /*
   MD5 initialization. Begins an MD5 operation, writing a new context. 
  */
  

  void
   MD5_init(MD5_CTX 
  *
   context)
{
    context
  ->
  count[
  0
  ] 
  =
   context
  ->
  count[
  1
  ] 
  =
   
  0
  ;
    
  /*
   Load magic initialization constants. 
  */
  
    context
  ->
  state[
  0
  ] 
  =
   
  0x67452301
  ;
    context
  ->
  state[
  1
  ] 
  =
   
  0xefcdab89
  ;
    context
  ->
  state[
  2
  ] 
  =
   
  0x98badcfe
  ;
    context
  ->
  state[
  3
  ] 
  =
   
  0x10325476
  ;
}


  /*
   MD5 block update operation. Continues an MD5 message-digest operation, 
   processing another message block, and updating the context. 
  */
  

  void
   MD5_update(MD5_CTX 
  *
   context, 
  const
   unsigned 
  char
   
  *
  input, unsigned 
  int
   inputLen)
{
    unsigned 
  int
   i, index, partLen;

    
  /*
   Compute number of bytes mod 64 
  */
  
    index 
  =
   (unsigned 
  int
  ) ((context
  ->
  count[
  0
  ] 
  >>
   
  3
  ) 
  &
   
  0x3F
  );

    
  /*
   Update number of bits 
  */
  
    
  if
   ((context
  ->
  count[
  0
  ] 
  +=
   ((unsigned 
  int
  ) inputLen 
  <<
   
  3
  )) 
  <
   ((unsigned 
  int
  ) inputLen 
  <<
   
  3
  ))
        context
  ->
  count[
  1
  ]
  ++
  ;
    context
  ->
  count[
  1
  ] 
  +=
   ((unsigned 
  int
  ) inputLen 
  >>
   
  29
  );

    partLen 
  =
   
  64
   
  -
   index;

    
  /*
   Transform as many times as possible. 
  */
  
    
  if
   (inputLen 
  >=
   partLen) {
        memcpy((
  void
   
  *
  ) 
  &
  context
  ->
  buffer[index], (
  void
   
  *
  ) input, partLen);
        _MD5Transform(context
  ->
  state, context
  ->
  buffer);

        
  for
   (i 
  =
   partLen; i 
  +
   
  63
   
  <
   inputLen; i 
  +=
   
  64
  )
            _MD5Transform(context
  ->
  state, 
  &
  input[i]);

        index 
  =
   
  0
  ;
    }
    
  else
  
        i 
  =
   
  0
  ;

    
  /*
   Buffer remaining input 
  */
  
    memcpy((
  void
   
  *
  ) 
  &
  context
  ->
  buffer[index], (
  void
   
  *
  ) 
  &
  input[i], inputLen 
  -
   i);
}


  /*
   MD5 finalization. Ends an MD5 message-digest operation, writing the message digest and zeroizing the context. 
  */
  

  void
   MD5_fini(unsigned 
  char
   digest[
  16
  ], MD5_CTX 
  *
   context)
{
    unsigned 
  char
   bits[
  8
  ];
    unsigned 
  int
   index, padLen;

    
  /*
   Save number of bits 
  */
  
    _Encode(bits, context
  ->
  count, 
  8
  );

    
  /*
   Pad out to 56 mod 64. 
  */
  
    index 
  =
   (unsigned 
  int
  ) ((context
  ->
  count[
  0
  ] 
  >>
   
  3
  ) 
  &
   
  0x3f
  );
    padLen 
  =
   (index 
  <
   
  56
  ) 
  ?
   (
  56
   
  -
   index) : (
  120
   
  -
   index);
    MD5_update(context, PADDING, padLen);

    
  /*
   Append length (before padding) 
  */
  
    MD5_update(context, bits, 
  8
  );

    
  /*
   Store state in digest 
  */
  
    _Encode(digest, context
  ->
  state, 
  16
  );

    
  /*
   Zeroize sensitive information.
  */
  
    memset((
  void
   
  *
  ) context, 
  0
  , 
  sizeof
   (
  *
  context));
}


  #pragma
   warning(push)    /* C4996 */
  

  #pragma
   warning( disable : 4996 )
  


  char
  *
   MD5_sign (
  const
   unsigned 
  char
   
  *
  str, unsigned 
  int
   len)
{
    
  int
   i;
    MD5_CTX     md5;
    
  static
   
  char
   md5_str[MD5STR_LEN
  +
  1
  ];
    
  char
   hash[
  16
  ], tmp[
  3
  ];
    md5_str[
  0
  ] 
  =
   
  0
  ;
            
    MD5_init(
  &
  md5);
    MD5_update (
  &
  md5, str, len);
    MD5_fini (hash, 
  &
  md5);
        
    
  for
   ( i 
  =
   
  0
   ; i 
  <
   
  16
   ; i
  ++
   )
    {
        _itoa((unsigned 
  char
  )hash[i], tmp , 
  16
  );

        
  if
   (tmp[
  1
  ] 
  ==
   
  0
  ){
            tmp[
  2
  ]
  =
  0
  ;    tmp[
  1
  ]
  =
  tmp[
  0
  ]; tmp[
  0
  ]
  =
  '
  0
  '
  ;
        }
        strcat(md5_str, tmp);
    }

    
  return
   md5_str;
}


  #pragma
   warning(pop)    /* C4996 */
  


  /*
   MD5 basic transformation. Transforms state based on block. 
  */
  

  static
   
  void
   _MD5Transform(unsigned 
  int
   state[
  4
  ], 
  const
   unsigned 
  char
   block[
  64
  ])
{
    unsigned 
  int
   a 
  =
   state[
  0
  ], 
                 b 
  =
   state[
  1
  ], 
                 c 
  =
   state[
  2
  ], 
                 d 
  =
   state[
  3
  ], 
                 x[
  16
  ];

    _Decode(x, block, 
  64
  );

    
  /*
   Round 1 
  */
  
    FF(a, b, c, d, x[
  0
  ], S11, 
  0xd76aa478
  );    
  /*
   1 
  */
  
    FF(d, a, b, c, x[
  1
  ], S12, 
  0xe8c7b756
  );    
  /*
   2 
  */
  
    FF(c, d, a, b, x[
  2
  ], S13, 
  0x242070db
  );    
  /*
   3 
  */
  
    FF(b, c, d, a, x[
  3
  ], S14, 
  0xc1bdceee
  );    
  /*
   4 
  */
  
    FF(a, b, c, d, x[
  4
  ], S11, 
  0xf57c0faf
  );    
  /*
   5 
  */
  
    FF(d, a, b, c, x[
  5
  ], S12, 
  0x4787c62a
  );    
  /*
   6 
  */
  
    FF(c, d, a, b, x[
  6
  ], S13, 
  0xa8304613
  );    
  /*
   7 
  */
  
    FF(b, c, d, a, x[
  7
  ], S14, 
  0xfd469501
  );    
  /*
   8 
  */
  
    FF(a, b, c, d, x[
  8
  ], S11, 
  0x698098d8
  );    
  /*
   9 
  */
  
    FF(d, a, b, c, x[
  9
  ], S12, 
  0x8b44f7af
  );    
  /*
   10 
  */
  
    FF(c, d, a, b, x[
  10
  ], S13, 
  0xffff5bb1
  );    
  /*
   11 
  */
  
    FF(b, c, d, a, x[
  11
  ], S14, 
  0x895cd7be
  );    
  /*
   12 
  */
  
    FF(a, b, c, d, x[
  12
  ], S11, 
  0x6b901122
  );    
  /*
   13 
  */
  
    FF(d, a, b, c, x[
  13
  ], S12, 
  0xfd987193
  );    
  /*
   14 
  */
  
    FF(c, d, a, b, x[
  14
  ], S13, 
  0xa679438e
  );    
  /*
   15 
  */
  
    FF(b, c, d, a, x[
  15
  ], S14, 
  0x49b40821
  );    
  /*
   16 
  */
  

    
  /*
   Round 2 
  */
  
    GG(a, b, c, d, x[
  1
  ], S21, 
  0xf61e2562
  );    
  /*
   17 
  */
  
    GG(d, a, b, c, x[
  6
  ], S22, 
  0xc040b340
  );    
  /*
   18 
  */
  
    GG(c, d, a, b, x[
  11
  ], S23, 
  0x265e5a51
  );    
  /*
   19 
  */
  
    GG(b, c, d, a, x[
  0
  ], S24, 
  0xe9b6c7aa
  );    
  /*
   20 
  */
  
    GG(a, b, c, d, x[
  5
  ], S21, 
  0xd62f105d
  );    
  /*
   21 
  */
  
    GG(d, a, b, c, x[
  10
  ], S22, 
  0x2441453
  );    
  /*
   22 
  */
  
    GG(c, d, a, b, x[
  15
  ], S23, 
  0xd8a1e681
  );    
  /*
   23 
  */
  
    GG(b, c, d, a, x[
  4
  ], S24, 
  0xe7d3fbc8
  );    
  /*
   24 
  */
  
    GG(a, b, c, d, x[
  9
  ], S21, 
  0x21e1cde6
  );    
  /*
   25 
  */
  
    GG(d, a, b, c, x[
  14
  ], S22, 
  0xc33707d6
  );    
  /*
   26 
  */
  
    GG(c, d, a, b, x[
  3
  ], S23, 
  0xf4d50d87
  );    
  /*
   27 
  */
  
    GG(b, c, d, a, x[
  8
  ], S24, 
  0x455a14ed
  );    
  /*
   28 
  */
  
    GG(a, b, c, d, x[
  13
  ], S21, 
  0xa9e3e905
  );    
  /*
   29 
  */
  
    GG(d, a, b, c, x[
  2
  ], S22, 
  0xfcefa3f8
  );    
  /*
   30 
  */
  
    GG(c, d, a, b, x[
  7
  ], S23, 
  0x676f02d9
  );    
  /*
   31 
  */
  
    GG(b, c, d, a, x[
  12
  ], S24, 
  0x8d2a4c8a
  );    
  /*
   32 
  */
  

    
  /*
   Round 3 
  */
  
    HH(a, b, c, d, x[
  5
  ], S31, 
  0xfffa3942
  );    
  /*
   33 
  */
  
    HH(d, a, b, c, x[
  8
  ], S32, 
  0x8771f681
  );    
  /*
   34 
  */
  
    HH(c, d, a, b, x[
  11
  ], S33, 
  0x6d9d6122
  );    
  /*
   35 
  */
  
    HH(b, c, d, a, x[
  14
  ], S34, 
  0xfde5380c
  );    
  /*
   36 
  */
  
    HH(a, b, c, d, x[
  1
  ], S31, 
  0xa4beea44
  );    
  /*
   37 
  */
  
    HH(d, a, b, c, x[
  4
  ], S32, 
  0x4bdecfa9
  );    
  /*
   38 
  */
  
    HH(c, d, a, b, x[
  7
  ], S33, 
  0xf6bb4b60
  );    
  /*
   39 
  */
  
    HH(b, c, d, a, x[
  10
  ], S34, 
  0xbebfbc70
  );    
  /*
   40 
  */
  
    HH(a, b, c, d, x[
  13
  ], S31, 
  0x289b7ec6
  );    
  /*
   41 
  */
  
    HH(d, a, b, c, x[
  0
  ], S32, 
  0xeaa127fa
  );    
  /*
   42 
  */
  
    HH(c, d, a, b, x[
  3
  ], S33, 
  0xd4ef3085
  );    
  /*
   43 
  */
  
    HH(b, c, d, a, x[
  6
  ], S34, 
  0x4881d05
  );    
  /*
   44 
  */
  
    HH(a, b, c, d, x[
  9
  ], S31, 
  0xd9d4d039
  );    
  /*
   45 
  */
  
    HH(d, a, b, c, x[
  12
  ], S32, 
  0xe6db99e5
  );    
  /*
   46 
  */
  
    HH(c, d, a, b, x[
  15
  ], S33, 
  0x1fa27cf8
  );    
  /*
   47 
  */
  
    HH(b, c, d, a, x[
  2
  ], S34, 
  0xc4ac5665
  );    
  /*
   48 
  */
  

    
  /*
   Round 4 
  */
  
    II(a, b, c, d, x[
  0
  ], S41, 
  0xf4292244
  );    
  /*
   49 
  */
  
    II(d, a, b, c, x[
  7
  ], S42, 
  0x432aff97
  );    
  /*
   50 
  */
  
    II(c, d, a, b, x[
  14
  ], S43, 
  0xab9423a7
  );    
  /*
   51 
  */
  
    II(b, c, d, a, x[
  5
  ], S44, 
  0xfc93a039
  );    
  /*
   52 
  */
  
    II(a, b, c, d, x[
  12
  ], S41, 
  0x655b59c3
  );    
  /*
   53 
  */
  
    II(d, a, b, c, x[
  3
  ], S42, 
  0x8f0ccc92
  );    
  /*
   54 
  */
  
    II(c, d, a, b, x[
  10
  ], S43, 
  0xffeff47d
  );    
  /*
   55 
  */
  
    II(b, c, d, a, x[
  1
  ], S44, 
  0x85845dd1
  );    
  /*
   56 
  */
  
    II(a, b, c, d, x[
  8
  ], S41, 
  0x6fa87e4f
  );    
  /*
   57 
  */
  
    II(d, a, b, c, x[
  15
  ], S42, 
  0xfe2ce6e0
  );    
  /*
   58 
  */
  
    II(c, d, a, b, x[
  6
  ], S43, 
  0xa3014314
  );    
  /*
   59 
  */
  
    II(b, c, d, a, x[
  13
  ], S44, 
  0x4e0811a1
  );    
  /*
   60 
  */
  
    II(a, b, c, d, x[
  4
  ], S41, 
  0xf7537e82
  );    
  /*
   61 
  */
  
    II(d, a, b, c, x[
  11
  ], S42, 
  0xbd3af235
  );    
  /*
   62 
  */
  
    II(c, d, a, b, x[
  2
  ], S43, 
  0x2ad7d2bb
  );    
  /*
   63 
  */
  
    II(b, c, d, a, x[
  9
  ], S44, 
  0xeb86d391
  );    
  /*
   64 
  */
  

    state[
  0
  ] 
  +=
   a;
    state[
  1
  ] 
  +=
   b;
    state[
  2
  ] 
  +=
   c;
    state[
  3
  ] 
  +=
   d;

    
  /*
   Zeroize sensitive information. 
  */
  
    memset((
  void
   
  *
  ) x, 
  0
  , 
  sizeof
   (x));
}


  /*
   Encodes input (unsigned int) into output (unsigned char). Assumes len is a multiple of 4. 
  */
  

  static
   
  void
   _Encode(unsigned 
  char
   
  *
  output, unsigned 
  int
   
  *
  input, unsigned 
  int
   len)
{
    unsigned 
  int
   i, j;

    
  for
   (i 
  =
   
  0
  , j 
  =
   
  0
  ; j 
  <
   len; i
  ++
  , j 
  +=
   
  4
  ) {
        output[j] 
  =
   (unsigned 
  char
  ) (input[i] 
  &
   
  0xff
  );
        output[j 
  +
   
  1
  ] 
  =
   (unsigned 
  char
  ) ((input[i] 
  >>
   
  8
  ) 
  &
   
  0xff
  );
        output[j 
  +
   
  2
  ] 
  =
   (unsigned 
  char
  ) ((input[i] 
  >>
   
  16
  ) 
  &
   
  0xff
  );
        output[j 
  +
   
  3
  ] 
  =
   (unsigned 
  char
  ) ((input[i] 
  >>
   
  24
  ) 
  &
   
  0xff
  );
    }
}


  /*
   Decodes input (unsigned char) into output (unsigned int). Assumes len is a multiple of 4.
  */
  

  static
   
  void
   _Decode(unsigned 
  int
   
  *
  output, 
  const
   unsigned 
  char
   
  *
  input, unsigned 
  int
   len)
{
    unsigned 
  int
   i, j;

    
  for
   (i 
  =
   
  0
  , j 
  =
   
  0
  ; j 
  <
   len; i
  ++
  , j 
  +=
   
  4
  ) {
        output[i] 
  =
   ((unsigned 
  int
  ) input[j]) 
  |
   (((unsigned 
  int
  ) input[j 
  +
   
  1
  ]) 
  <<
   
  8
  ) 
  |
  
                    (((unsigned 
  int
  ) input[j 
  +
   
  2
  ]) 
  <<
   
  16
  ) 
  |
   (((unsigned 
  int
  ) input[j 
  +
   
  3
  ]) 
  <<
   
  24
  );
    }
}


        uuid32.c文件如下:

/*
   uuid32.c 
   2007-09-15 Last created by cheungmine.
   Partly rights reserved by cheungmine.

  */
  

  
#include 
  <
  stdio.h
  >
  
#include 
  <
  string
  .h
  >
  

#include 
  <
  time.h
  >
  
#include 
  <
  sys
  /
  types.h
  >
  
#include 
  <
  sys
  /
  timeb.h
  >
  

#include 
  "
  uuid32.h
  "
  
#include 
  "
  md5.h
  "
  



  #define
   MD5_LEN            16
  

  #define
   UUID_LEN        36
  


  /*
   microsecond per second. 1s=1000000us=1000000000ns
  */
  

  #define
   NSec100_Per_Sec        10000000
  

  #define
   USec_Per_Sec        1000000
  

  #define
   USec_Per_MSec        1000
  

  #define
   NSec_Since_1582        ((uint64)(0x01B21DD213814000))
  



  /*
  ========================================================================================
                            Private Functions
========================================================================================
  */
  

  static
   BOOL isbigendian()
{
    
  int
   c 
  =
   
  1
  ;
    
  return
   ( 
  *
  ((unsigned 
  char
   
  *
  ) 
  &
  c) 
  ==
   
  1
   )
  ?
   FALSE: TRUE;
};


  static
   
  void
   swap_word( 
  int
   size_bytes, 
  void
   
  *
   ptr_word )
{
    
  int
          i;
    unsigned 
  char
         temp;
    
  for
  ( i
  =
  0
  ; i 
  <
   size_bytes
  /
  2
  ; i
  ++
   )
    {
        temp 
  =
   ((unsigned 
  char
   
  *
  ) ptr_word)[i];
        ((unsigned 
  char
   
  *
  ) ptr_word)[i] 
  =
   ((unsigned 
  char
   
  *
  ) ptr_word)[size_bytes
  -
  i
  -
  1
  ];
        ((unsigned 
  char
   
  *
  ) ptr_word)[size_bytes
  -
  i
  -
  1
  ] 
  =
   temp;
    }
};


  static
   
  void
   write_word( unsigned 
  char
  *
   stream, word_t val )
{
    memcpy(stream, 
  &
  val, 
  2
  );
    
  if
  ( isbigendian() ) swap_word( 
  2
  , stream );
};


  static
   
  void
   write_dword( unsigned 
  char
  *
   stream, dword_t val )
{
    memcpy(stream, 
  &
  val, 
  4
  );
    
  if
  ( isbigendian() ) swap_word( 
  4
  , stream );
};


  static
   
  void
    read_word( 
  const
   unsigned 
  char
  *
   stream, word_t
  *
   val )
{
    memcpy( val, stream, 
  2
   );
    
  if
  ( isbigendian() )    swap_word( 
  2
  , val );
};


  static
   
  void
    read_dword( 
  const
   unsigned 
  char
  *
   stream, dword_t
  *
   val )
{
    memcpy( val, stream, 
  4
   );
    
  if
  ( isbigendian() )    swap_word( 
  4
  , val );
};


  static
   BOOL is_xdigit(
  char
   c)
{
    
  /*
   isxdigit returns a non-zero value if c is a hexadecimal digit (A – F, a – f, or 0 – 9). 
  */
  
    
  return
   ((c
  >=
  '
  A
  '
  &&
  c
  <=
  '
  F
  '
  )
  ||
  (c
  >=
  '
  a
  '
  &&
  c
  <=
  '
  f
  '
  )
  ||
  (c
  >=
  '
  0
  '
  &&
  c
  <=
  '
  9
  '
  ))
  ?
   TRUE : FALSE;
};



  /*
   make a pseudorandom numbel based on current time
  */
  

  static
   
  int
   pseudo_rand()
{
#ifdef _USE_32BIT_TIME_T
    assert(
  0
  );

  #endif
  

    
  struct
   _timeb  timebuf;


  #pragma
   warning(push)    /* C4996 */
  

  #pragma
   warning( disable : 4996 )
  
    _ftime64(
  &
  timebuf);

  #pragma
   warning(pop)    /* C4996 */
  
    
    srand((uint32) ((((uint32)timebuf.time
  &
  0xFFFF
  )
  +
  (uint32)timebuf.millitm)
  ^
  (uint32)timebuf.millitm));

    
  return
   rand();
};



  /*
  ========================================================================================
                            Public Functions
========================================================================================
  */
  

BOOL is_uuid_string(
  const
   
  char
   
  *
  uuid) 
{    
    
  static
   
  const
   
  char
   fmt[] 
  =
   
  "
  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  "
  ;
    
  int
   i;
    assert(uuid 
  !=
   NULL);
    
  for
   (i 
  =
   
  0
  ; i 
  <
   
  sizeof
  (fmt); i
  ++
  )
        
  if
   (fmt[i] 
  ==
   
  '
  x
  '
  )
            
  if
   (
  !
  is_xdigit(uuid[i]))
                
  return
   FALSE;
        
  else
   
  if
   (uuid[i] 
  !=
   fmt[i])
            
  return
   FALSE;

    
  return
   TRUE;
}



  /*
  *
 * internal
 * ingroup uuid
 * The thread synchronization lock used to guarantee UUID uniqueness
 * for all the threads running within a process.
 
  */
  

  void
   uuid_create(uuid_t
  *
   u) 
{    
    
  static
   BOOL        initialized 
  =
   FALSE;
    
  static
   int64    timestamp;
    
  static
   uint32    advance;
    
  static
   uint16    clockseq;
    
  static
   uint16    node_high;
    
  static
   uint32    node_low;
    int64            time;    
  /*
   unit of 100ns 
  */
  
    uint16            nowseq;
    
  int
                  r;

    #ifdef _USE_32BIT_TIME_T
        assert(
  0
  );
    
  #endif
  

    
  struct
   _timeb  tv;

    assert(u);


  #pragma
   warning(push)    /* C4996 */
  

  #pragma
   warning( disable : 4996 )
  
    _ftime64(
  &
  tv);

  #pragma
   warning(pop)    /* C4996 */
  

    
  /*
   time is counter of 100ns time interval since Oct.15, 1582 (NOT 1852) 
  */
  
    time 
  =
   ((uint64) tv.time) 
  *
   USec_Per_Sec 
  +
   ((uint64) tv.millitm
  *
  USec_Per_MSec);
    time 
  =
   time 
  *
   
  10
   
  +
   NSec_Since_1582;

    
  if
   (
  !
  initialized) 
    {
        timestamp 
  =
   time;
        advance 
  =
   
  0
  ;

        r 
  =
   pseudo_rand();

        clockseq 
  =
   r 
  >>
   
  16
  ;
        node_high 
  =
   r 
  |
   
  0x0100
  ;
        
        node_low 
  =
   pseudo_rand();
        
        initialized 
  =
   TRUE;
    } 
    
  else
   
  if
   (time 
  <
   timestamp) 
    {
        timestamp 
  =
   time;
        advance 
  =
   
  0
  ;
        clockseq
  ++
  ;
    } 
    
  else
   
  if
   (time 
  ==
   timestamp) 
    {
        advance
  ++
  ;
        time 
  +=
   advance;
    } 
    
  else
   
    {
        timestamp 
  =
   time;
        advance 
  =
   
  0
  ;
    }
    nowseq 
  =
   clockseq;

    assert(u);
    u
  ->
  data1 
  =
   (dword_t) time;
    u
  ->
  data2 
  =
   (word_t) ((time 
  >>
   
  32
  ) 
  &
   
  0xffff
  );
    u
  ->
  data3 
  =
   (word_t) (((time 
  >>
   
  48
  ) 
  &
   
  0x0ffff
  ) 
  |
   
  0x1000
  );
    write_word(
  &
  (u
  ->
  data4[
  6
  ]), (word_t) ((nowseq 
  &
   
  0x3fff
  ) 
  |
   
  0x8000
  ));    
    write_word(
  &
  (u
  ->
  data4[
  4
  ]), (word_t) (node_high));                    
    write_dword(
  &
  (u
  ->
  data4[
  0
  ]), (dword_t) (node_low));            
}


  /*
  *
 * internal
 * ingroup uuid
 * The thread synchronization lock used to guarantee UUID uniqueness
 * for all the threads running within a process.
 
  */
  

  char
   
  *
  uuid_create_string(
  void
  ) 
{
    uuid_t  u;
    uuid_create(
  &
  u);
    
  return
   uuid_to_string(
  &
  u);
}


  char
   
  *
  uuid_to_string(
  const
   uuid_t
  *
    u)
{
    
  static
   
  char
   uuid_str[UUID_LEN
  +
  1
  ];
    
  ushort
   a,b;
    uint32  c;
    read_word(
  &
  (u
  ->
  data4[
  6
  ]), 
  &
  a);
    read_word(
  &
  (u
  ->
  data4[
  4
  ]), 
  &
  b);
    read_dword(
  &
  (u
  ->
  data4[
  0
  ]), 
  &
  c);


  #pragma
   warning(push)    /* C4996 */
  

  #pragma
   warning( disable : 4996 )
  
    sprintf(uuid_str, 
  "
  %08lx-%04x-%04x-%04x-%04x%08lx
  "
  , 
                u
  ->
  data1,
                u
  ->
  data2,
                u
  ->
  data3,
                a, b, c);

  #pragma
   warning(pop)    /* C4996 */
  
    
  return
   uuid_str;
}


  /*
  *
 * internal
 * ingroup uuid
 * The predefined namespace UUID. Expressed in binary format
 * to avoid unnecessary conversion when generating name based UUIDs.
 
  */
  

  static
   
  const
   unsigned 
  char
   namespace_uuid[] 
  =
   {
        
  0x9c
  , 
  0xfb
  , 
  0xd9
  , 
  0x1f
  , 
  0x11
  , 
  0x72
  , 
  0x4a
  , 
  0xf6
  ,
        
  0xbd
  , 
  0xcb
  , 
  0x9f
  , 
  0x34
  , 
  0xe4
  , 
  0x6f
  , 
  0xa0
  , 
  0xfb
  
};


  void
    uuid_create_external(
  const
   
  char
   
  *
  external, uuid_t
  *
   u) 
{
    MD5_CTX md5;
    unsigned 
  char
   uuid[
  16
  ];    
    
    assert(external 
  !=
   NULL);

    MD5_init(
  &
  md5);
    MD5_update(
  &
  md5, namespace_uuid, 
  sizeof
  (namespace_uuid));
    MD5_update(
  &
  md5, (unsigned 
  char
   
  *
  ) external, (unsigned 
  int
  ) strlen(external));
    MD5_fini(uuid, 
  &
  md5);

    u
  ->
  data1 
  =
   (dword_t) (uuid[
  0
  ] 
  <<
   
  24
   
  |
   uuid[
  1
  ] 
  <<
   
  16
   
  |
   uuid[
  2
  ] 
  <<
   
  8
   
  |
   uuid[
  3
  ]);
    u
  ->
  data2 
  =
   (word_t)  (uuid[
  4
  ] 
  <<
   
  8
   
  |
   uuid[
  5
  ]);
    u
  ->
  data3 
  =
   (word_t)  (((uuid[
  6
  ] 
  &
   
  0x0f
  ) 
  |
   
  0x30
  ) 
  <<
   
  8
   
  |
   uuid[
  7
  ]);    
    
    
  /*
   BYTE 6-7 
  */
  
    write_word(
  &
  (u
  ->
  data4[
  6
  ]), (word_t) (((uuid[
  8
  ] 
  &
   
  0x3f
  ) 
  |
   
  0x80
  ) 
  <<
   
  8
   
  |
   uuid[
  9
  ]));        
    
  /*
   BYTE 4-5 
  */
  
    write_word(
  &
  (u
  ->
  data4[
  4
  ]), (word_t) (uuid[
  10
  ] 
  <<
   
  8
   
  |
   uuid[
  11
  ]));                        
    
  /*
   BYTE 0-3 
  */
  
    write_dword(
  &
  (u
  ->
  data4[
  0
  ]), (dword_t) (uuid[
  12
  ] 
  <<
   
  24
   
  |
   uuid[
  13
  ] 
  <<
   
  16
   
  |
   uuid[
  14
  ] 
  <<
   
  8
   
  |
   uuid[
  15
  ]));
}


  /*
  *
 * Get timestamp from a UUID
 *
  */
  

  void
   uuid_to_timestamp(
  const
   uuid_t
  *
   u, timestamp_t
  *
   t)
{
    int64   time, t2, t3;
    
  struct
    tm
  *
    p;
    assert(u);

    t2 
  =
   u
  ->
  data2;
    t3 
  =
   u
  ->
  data3;

    time 
  =
   u
  ->
  data1 
  +
   (t2
  <<
  32
  ) 
  +
   ((t3
  &
  0x0fff
  )
  <<
  48
  );        
  /*
   100ns 
  */
  
    time 
  -=
   NSec_Since_1582;

    t
  ->
  tm_fraction 
  =
   (
  long
  )(time
  %
  NSec100_Per_Sec);
    
    time 
  /=
   
  10
  ;
    time 
  /=
   USec_Per_Sec; 
    

  #pragma
   warning(push)    /* C4996 */
  

  #pragma
   warning( disable : 4996 )
  
    p 
  =
   _localtime64(
  &
  time);

  #pragma
   warning(pop)    /* C4996 */
  
    
    t
  ->
  tm_hour 
  =
   p
  ->
  tm_hour;
    t
  ->
  tm_mday 
  =
   p
  ->
  tm_mday;
    t
  ->
  tm_min 
  =
   p
  ->
  tm_min;
    t
  ->
  tm_mon 
  =
   p
  ->
  tm_mon;
    t
  ->
  tm_sec 
  =
   p
  ->
  tm_sec;
    t
  ->
  tm_wday 
  =
   p
  ->
  tm_wday;
    t
  ->
  tm_yday 
  =
   p
  ->
  tm_yday;
    t
  ->
  tm_year 
  =
   p
  ->
  tm_year;
}


  char
  *
   timestamp_to_string(
  const
   timestamp_t
  *
   time)
{
    
  struct
   tm t;
    t.tm_hour 
  =
   time
  ->
  tm_hour;
    t.tm_mday 
  =
   time
  ->
  tm_mday;
    t.tm_min 
  =
   time
  ->
  tm_min;
    t.tm_mon 
  =
   time
  ->
  tm_mon;
    t.tm_sec 
  =
   time
  ->
  tm_sec;
    t.tm_wday 
  =
   time
  ->
  tm_wday;
    t.tm_yday 
  =
   time
  ->
  tm_yday;
    t.tm_year 
  =
   time
  ->
  tm_year;


  #pragma
   warning(push)    /* C4996 */
  

  #pragma
   warning( disable : 4996 )
  
    
  return
   asctime(
  &
  t);

  #pragma
   warning(pop)    /* C4996 */
  
}




  /*
  *
 * Compare two UUID's lexically
 *    return
 *      -1   u1 is lexically before u2
 *     0   u1 is equal to u2
 *     1   u1 is lexically after u2

  */
  

  int
   uuid_compare(
  const
   uuid_t 
  *
  u1, 
  const
   uuid_t 
  *
  u2)
{
    
  int
   i;


  #define
   CHECK_COMP(f1, f2)  if ((f1) != (f2)) return ((f1) < (f2) ? -1 : 1);
  
    
    CHECK_COMP(u1
  ->
  data1, u2
  ->
  data1);
    CHECK_COMP(u1
  ->
  data2, u2
  ->
  data2);
    CHECK_COMP(u1
  ->
  data3, u2
  ->
  data3);

    
  for
  (i
  =
  0
  ; i
  <
  8
  ; i
  ++
  )
        CHECK_COMP(u1
  ->
  data4[i], u1
  ->
  data4[i]);


  #undef
   CHECK_COMP
  

    
  return
   
  0
  ;
}


  /*
  *
 * Compare two UUID's temporally
 *    return
 *      -1   u1 is temporally before u2
 *     0   u1 is equal to u2
 *     1   u1 is temporally after u2

  */
  

  int
   uuid_compare_time(
  const
   uuid_t 
  *
  u1, 
  const
   uuid_t 
  *
  u2)
{    

  #define
   CHECK_COMP(f1, f2)  if ((f1) != (f2)) return ((f1) < (f2) ? -1 : 1);
  
    
    CHECK_COMP(u1
  ->
  data1, u2
  ->
  data1);
    CHECK_COMP(u1
  ->
  data2, u2
  ->
  data2);
    CHECK_COMP(u1
  ->
  data3, u2
  ->
  data3);


  #undef
   CHECK_COMP
  

    
  return
   
  0
  ;
}


      好了,到此,所有文件都列出来了,它们是:cdatatype.h、md5.h、uuid32.h、md5.c和uuid32.c。

最后是测试代码:

 

/*
   uuidgen.c 
   2007-09-15 Last created by cheungmine.
   All rights reserved by cheungmine.
   C application

  */
  
#include 
  <
  stdio.h
  >
  
#include 
  <
  stdlib.h
  >
  
#include 
  <
  string
  .h
  >
  

#include 
  "
  md5.h
  "
  
#include 
  "
  uuid32.h
  "
  


  int
   main()
{
    
  char
   
  *
  sign, 
  *
  uid;
    uuid_t u, v, x;
    timestamp_t  t;
    
    sign 
  =
   MD5_sign(
  "
  hello world
  "
  , (unsigned 
  int
  )strlen(
  "
  hello world
  "
  ));
    printf(
  "
  md5 string digit:%s 
  "
  , sign);
    
    uuid_create(
  &
  u);
    uid 
  =
   uuid_to_string(
  &
  u);
    printf(
  "
  uuid U to string:{%s} 
  "
  , uid);

    uuid_create(
  &
  v);
    uid 
  =
   uuid_to_string(
  &
  v);
    printf(
  "
  uuid V to string:{%s} 
  "
  , uid);
    
    printf(
  "
  uuid compare U with V lexically:%d 
  "
  , uuid_compare(
  &
  u, 
  &
  v));

    printf(
  "
  uuid compare V with U temporally:%d 
  "
  , uuid_compare_time(
  &
  v, 
  &
  u));


    uid 
  =
   uuid_create_string();
    printf(
  "
  new uuid string:{%s} 
  "
  , uid);
        
    uuid_create_external(
  "
  cheungmine
  "
  , 
  &
  x);
    uid 
  =
   uuid_to_string(
  &
  x);
    printf(
  "
  new external uuid to string:{%s} 
  "
  , uid);
        
    uuid_to_timestamp(
  &
  u, 
  &
  t);
    printf(
  "
  %s 
  "
  , timestamp_to_string(
  &
  t));
    
    
  return
   
  0
  ;
}


          以上代码保证正确。请放心使用!