Add Timestamp::EncodeStringWithOffset() for representation outside of UTC.
[asdcplib.git] / src / KM_util.cpp
1 /*
2 Copyright (c) 2005-2009, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27   /*! \file    KM_util.cpp
28     \version $Id$
29     \brief   Utility functions
30   */
31
32 #include <KM_util.h>
33 #include <KM_prng.h>
34 #include <KM_memio.h>
35 #include <KM_fileio.h>
36 #include <KM_log.h>
37 #include <KM_tai.h>
38 #include <ctype.h>
39 #include <list>
40 #include <map>
41 #include <string>
42
43 const char*
44 Kumu::Version()
45 {
46   return PACKAGE_VERSION;
47 }
48
49
50 //------------------------------------------------------------------------------------------
51
52 // Result_t Internals
53
54 struct map_entry_t
55 {
56   int             rcode;
57   Kumu::Result_t* result;
58 };
59
60 const ui32_t MapMax = 1024;
61 const ui32_t MapSize = MapMax * (sizeof(struct map_entry_t));
62 static bool s_MapInit = false;
63 static struct map_entry_t s_ResultMap[MapSize];
64
65 //
66 const Kumu::Result_t&
67 Kumu::Result_t::Find(int v)
68 {
69   if ( v == 0 )
70     return RESULT_OK;
71
72   for ( ui32_t i = 0; s_ResultMap[i].result != 0 && i < MapMax; i++ )
73     {
74       if ( s_ResultMap[i].rcode == v )
75         return *s_ResultMap[i].result;
76     }
77
78   return RESULT_UNKNOWN;
79 }
80
81 //
82 Kumu::Result_t
83 Kumu::Result_t::Delete(int v)
84 {
85   if ( v >= RESULT_NOTAFILE.Value() )
86     {
87       DefaultLogSink().Error("Cannot delete core result code: %ld\n", v);
88       return RESULT_FAIL;
89     }
90
91   for ( ui32_t i = 0; s_ResultMap[i].result != 0 && i < MapMax; i++ )
92     {
93       if ( s_ResultMap[i].rcode == v )
94         {
95           s_ResultMap[i].rcode = 0;
96           s_ResultMap[i++].result = 0;
97
98           for ( ; s_ResultMap[i].result != 0 && i < MapMax; i++ )
99             s_ResultMap[i-1] = s_ResultMap[i];
100
101           return RESULT_OK;
102         }
103     }
104
105   return RESULT_FALSE;
106 }
107
108
109 //
110 Kumu::Result_t::Result_t(int v, const char* l) : value(v), label(l)
111 {
112   assert(l);
113   assert(value < (int)MapMax);
114
115   if ( v == 0 )
116     return;
117
118   if ( ! s_MapInit )
119     {
120       s_MapInit = true;
121       s_ResultMap[0].rcode = v;
122       s_ResultMap[0].result = this;
123       s_ResultMap[1].rcode = 0;
124       s_ResultMap[1].result = 0;
125       return;
126     }
127
128   ui32_t i = 0;
129   while ( s_ResultMap[i].result != 0 && i < MapMax )
130     {
131       if ( s_ResultMap[i].rcode == v && s_ResultMap[i].result != 0 )
132         return;
133
134       i++;
135     }
136
137   assert(i+2 < MapMax);
138
139   s_ResultMap[i].rcode = v;
140   s_ResultMap[i].result = this;
141   s_ResultMap[i+1].rcode = 0;
142   s_ResultMap[i+1].result = 0;
143   return;
144 }
145
146 Kumu::Result_t::~Result_t() {}
147
148
149 //------------------------------------------------------------------------------------------
150 // DTrace internals
151
152 static int s_DTraceSequence = 0;
153
154 Kumu::DTrace_t::DTrace_t(const char* Label, Kumu::Result_t* Watch, int Line, const char* File)
155   : m_Label(Label), m_Watch(Watch), m_Line(Line), m_File(File)
156 {
157   m_Sequence = s_DTraceSequence++;
158   DefaultLogSink().Debug("@enter %s[%d] (%s at %d)\n", m_Label, m_Sequence, m_File, m_Line);
159 }
160
161 Kumu::DTrace_t::~DTrace_t()
162 {
163   if ( m_Watch != 0  )
164     DefaultLogSink().Debug("@exit %s[%d]: %s\n", m_Label, m_Sequence, m_Watch->Label());
165   else
166     DefaultLogSink().Debug("@exit %s[%d]\n", m_Label, m_Sequence);
167 }
168
169 //------------------------------------------------------------------------------------------
170
171
172 const char  fill = '=';
173 const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
174
175 const byte_t decode_map[] =
176 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
177   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
178   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
179   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
180   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
181   0xff, 0xff, 0xff, 62,   0xff, 0xff, 0xff, 63,
182   52,   53,   54,   55,   56,   57,   58,   59,
183   60,   61,   0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
184   0xff, 0,    1,    2,    3,    4,    5,    6,
185   7,    8,    9,    10,   11,   12,   13,   14,
186   15,   16,   17,   18,   19,   20,   21,   22,
187   23,   24,   25,   0xff, 0xff, 0xff, 0xff, 0xff,
188   0xff, 26,   27,   28,   29,   30,   31,   32,
189   33,   34,   35,   36,   37,   38,   39,   40,
190   41,   42,   43,   44,   45,   46,   47,   48,
191   49,   50,   51,   0xff, 0xff, 0xff, 0xff, 0xff,
192   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
193   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
194   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
195   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
196   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
197   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
198   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
199   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
200   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
201   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
202   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
203   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
204   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
205   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
206   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
207   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
208 };
209
210
211 // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer
212 // if the binary buffer was large enough to hold the result. If the output buffer
213 // is too small or any of the pointer arguments are NULL, the subroutine will
214 // return 0.
215 //
216 const char*
217 Kumu::base64encode(const byte_t* buf, ui32_t buf_len, char* strbuf, ui32_t strbuf_len)
218 {
219   ui32_t out_char = 0;
220   ui32_t i, block_len, diff;
221
222   if ( buf == 0 || strbuf == 0 )
223     return 0;
224
225   if ( strbuf_len < base64_encode_length(buf_len) + 1 )
226     return 0;
227
228   block_len = buf_len;
229
230   while ( block_len % 3 )
231     block_len--;
232
233   for ( i = 0; i < block_len; i += 3 )
234     {
235       strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
236       strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
237       strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) | ( buf[2] >> 6 ) )];
238       strbuf[out_char++] = base64_chars[( buf[2] & 0x3f )];
239       buf += 3;
240     }
241
242   if ( i < buf_len )
243     {
244       diff = buf_len - i;
245       assert(diff > 0);
246       assert(diff < 3);
247       
248       strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
249
250       if ( diff == 1 )
251         {
252           strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) )];
253           strbuf[out_char++] = fill;
254         }
255       else if ( diff == 2 )
256         {
257           strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
258           strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) )];
259         }
260
261       strbuf[out_char++] = fill;
262     }
263
264   strbuf[out_char] = 0;
265   return strbuf;;
266 }
267
268
269
270
271 // Convert NULL-terminated UTF-8 Base64 string to binary, returns 0 if
272 // the binary buffer was large enough to hold the result. The output parameter
273 // 'char_count' will contain the length of the converted string. If the output
274 // buffer is too small or any of the pointer arguments are NULL, the subroutine
275 // will return -1 and set 'char_count' to the required buffer size. No data will
276 // be written to 'buf' if the subroutine fails.
277 //
278 i32_t
279 Kumu::base64decode(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count)
280 {
281   register byte_t c = 0, d = 0;
282   register ui32_t phase = 0, i = 0;
283
284   if ( str == 0 || buf == 0 || char_count == 0 )
285     return -1;
286
287   while ( *str != 0 && i < buf_len )
288     {
289       c = decode_map[(int)*str++];
290       if ( c == 0xff ) continue;
291       if ( c == 0xfe ) break;
292
293       switch ( phase++ )
294         {
295         case 0:
296           buf[i++] =  c << 2;
297           break;
298
299         case 1:
300           buf[i - 1] |= c >> 4;
301           d = c;
302           break;
303
304         case 2:
305           buf[i++] =  ( d << 4 ) | ( c >> 2 );
306           d = c;
307           break;
308
309         case 3:
310           buf[i++] =  ( d << 6 ) | c;
311           phase = 0;
312           break;
313         }
314     }
315
316   *char_count = i;
317   return 0;
318 }
319
320 //------------------------------------------------------------------------------------------
321
322 // convert utf-8 hext string to bin
323 i32_t
324 Kumu::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size)
325 {
326   KM_TEST_NULL_L(str);
327   KM_TEST_NULL_L(buf);
328   KM_TEST_NULL_L(conv_size);
329
330   *conv_size = 0;
331
332   if ( str[0] == 0 ) // nothing to convert
333     return 0;
334
335   for ( int j = 0; str[j]; j++ )
336     {
337       if ( isxdigit(str[j]) )
338         (*conv_size)++;
339     }
340
341   if ( *conv_size & 0x01 ) (*conv_size)++;
342   *conv_size /= 2;
343
344   if ( *conv_size > buf_len )// maximum possible data size
345     return -1;
346
347   *conv_size = 0;
348
349   int phase = 0; // track high/low nybble
350
351   // for each character, fill in the high nybble then the low
352   for ( int i = 0; str[i]; i++ )
353     {
354       if ( ! isxdigit(str[i]) )
355         continue;
356
357       byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) );
358
359       if ( phase == 0 )
360         {
361           buf[*conv_size] = val << 4;
362           phase++;
363         }
364       else
365         {
366           buf[*conv_size] |= val;
367           phase = 0;
368           (*conv_size)++;
369         }
370     }
371
372   return 0;
373 }
374
375 #ifdef CONFIG_RANDOM_UUID
376
377 // convert a memory region to a NULL-terminated hexadecimal string
378 //
379 static const char*
380 bin2hex_rand(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
381 {
382   if ( bin_buf == 0
383        || str_buf == 0
384        || ((bin_len * 2) + 1) > str_len )
385     return 0;
386
387   char* p = str_buf;
388   Kumu::mem_ptr<byte_t> rand_buf = new byte_t[bin_len];
389   Kumu::FortunaRNG RNG;
390   RNG.FillRandom(rand_buf, bin_len);
391
392   for ( ui32_t i = 0; i < bin_len; i++ )
393     {
394       *p = (bin_buf[i] >> 4) & 0x0f;
395       *p += *p < 10 ? 0x30 : (( ((rand_buf[i] & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
396       p++;
397
398       *p = bin_buf[i] & 0x0f;
399       *p += *p < 10 ? 0x30 : (( (((rand_buf[i] >> 1) & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
400       p++;
401     }
402
403   *p = '\0';
404   return str_buf;
405 }
406 #endif
407
408 // convert a memory region to a NULL-terminated hexadecimal string
409 //
410 const char*
411 Kumu::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
412 {
413   if ( bin_buf == 0
414        || str_buf == 0
415        || ((bin_len * 2) + 1) > str_len )
416     return 0;
417
418 #ifdef CONFIG_RANDOM_UUID
419   const char* use_random_uuid  = getenv("KM_USE_RANDOM_UUID");
420   if ( use_random_uuid != 0 && use_random_uuid[0] != 0 && use_random_uuid[0] != '0' )
421     return bin2hex_rand(bin_buf, bin_len, str_buf, str_len);
422 #endif
423
424   char* p = str_buf;
425
426   for ( ui32_t i = 0; i < bin_len; i++ )
427     {
428       *p = (bin_buf[i] >> 4) & 0x0f;
429       *p += *p < 10 ? 0x30 : 0x61 - 10;
430       p++;
431
432       *p = bin_buf[i] & 0x0f;
433       *p += *p < 10 ? 0x30 : 0x61 - 10;
434       p++;
435     }
436
437   *p = '\0';
438   return str_buf;
439 }
440
441
442 // spew a range of bin data as hex
443 void
444 Kumu::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream)
445 {
446   if ( buf == 0 )
447     return;
448
449   if ( stream == 0 )
450     stream = stderr;
451
452   static ui32_t row_len = 16;
453   const byte_t* p = buf;
454   const byte_t* end_p = p + dump_len;
455
456   for ( ui32_t line = 0; p < end_p; line++ )
457     {
458       fprintf(stream, "  %06x: ", line);
459       ui32_t i;
460       const byte_t* pp;
461
462       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
463         fprintf(stream, "%02x ", *pp);
464
465       while ( i++ < row_len )
466         fputs("   ", stream);
467
468       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
469         fputc((isprint(*pp) ? *pp : '.'), stream);
470
471       fputc('\n', stream);
472       p += row_len;
473     }
474 }
475
476 //
477 const char*
478 Kumu::bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
479 {
480   ui32_t i, j, k;
481
482   if ( str_len < 34 || bin_len != UUID_Length )
483     return 0;
484
485   if ( bin2hex(bin_buf, bin_len, str_buf, str_len) == 0 )
486     return 0;
487
488   // shift the node id
489   for ( k = 19, i = 12; i > 0; i-- )
490     str_buf[k+i+4] = str_buf[k+i];
491
492   // shift the time (mid+hi+clk)
493   for ( k = 15, j = 3; k > 6; k -= 4, j-- )
494     {
495       for ( i = 4; i > 0; i-- )
496         str_buf[k+i+j] = str_buf[k+i];
497     }
498
499   // add in the hyphens and trainling null
500   for ( i = 8; i < 24; i += 5 )
501     str_buf[i] = '-';
502   
503   str_buf[36] = 0;
504   return str_buf;
505 }
506
507 //
508 void
509 Kumu::GenRandomValue(UUID& ID)
510 {
511   byte_t tmp_buf[UUID_Length];
512   GenRandomUUID(tmp_buf);
513   ID.Set(tmp_buf);
514 }
515
516 //
517 void
518 Kumu::GenRandomUUID(byte_t* buf)
519 {
520   FortunaRNG RNG;
521   RNG.FillRandom(buf, UUID_Length);
522   buf[6] &= 0x0f; // clear bits 4-7
523   buf[6] |= 0x40; // set UUID version
524   buf[8] &= 0x3f; // clear bits 6&7
525   buf[8] |= 0x80; // set bit 7
526 }
527
528 //
529 void
530 Kumu::GenRandomValue(SymmetricKey& Key)
531 {
532   byte_t tmp_buf[SymmetricKey_Length];
533   FortunaRNG RNG;
534   RNG.FillRandom(tmp_buf, SymmetricKey_Length);
535   Key.Set(tmp_buf);
536 }
537
538
539 //------------------------------------------------------------------------------------------
540 // read a ber value from the buffer and compare with test value.
541 // Advances buffer to first character after BER value.
542
543 // read a ber value from the buffer and compare with test value.
544 // Advances buffer to first character after BER value.
545 //
546 bool
547 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
548 {
549   if ( buf == 0 )
550     return false;
551
552   if ( ( **buf & 0x80 ) == 0 )
553     return false;
554
555   ui64_t val = 0;
556   ui8_t ber_size = ( **buf & 0x0f ) + 1;
557
558   if ( ber_size > 9 )
559     return false;
560
561   for ( ui8_t i = 1; i < ber_size; i++ )
562     {
563       if ( (*buf)[i] > 0 )
564         val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
565     }
566
567   *buf += ber_size;
568   return ( val == test_value );
569 }
570
571
572 //
573 bool
574 Kumu::read_BER(const byte_t* buf, ui64_t* val)
575 {
576   ui8_t ber_size, i;
577   
578   if ( buf == 0 || val == 0 )
579     return false;
580
581   if ( ( *buf & 0x80 ) == 0 )
582     return false;
583
584   *val = 0;
585   ber_size = ( *buf & 0x0f ) + 1;
586
587   if ( ber_size > 9 )
588     return false;
589
590   for ( i = 1; i < ber_size; i++ )
591     {
592       if ( buf[i] > 0 )
593         *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
594     }
595
596   return true;
597 }
598
599
600 static const ui64_t ber_masks[9] =
601   { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), 
602     ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
603     ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
604     ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
605     0
606   };
607
608
609 //
610 bool
611 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
612 {
613   if ( buf == 0 )
614     return false;
615
616   if ( ber_len == 0 )
617     { // calculate default length
618       if ( val < 0x01000000L )
619         ber_len = 4;
620       else if ( val < ui64_C(0x0100000000000000) )
621         ber_len = 8;
622       else
623         ber_len = 9;
624     }
625   else
626     { // sanity check BER length
627       if ( ber_len > 9 )
628         {
629           DefaultLogSink().Error("BER size %u exceeds maximum size of 9\n", ber_len);
630           return false;
631         }
632       
633       if ( val & ber_masks[ber_len - 1] )
634         {
635           ui64Printer tmp_i(val);
636           DefaultLogSink().Error("BER size %u too small for value %s\n", tmp_i.c_str());
637           return false;
638         }
639     }
640
641   buf[0] = 0x80 + ( ber_len - 1 );
642
643   for ( ui32_t i = ber_len - 1; i > 0; i-- )
644     {
645       buf[i] = (ui8_t)(val & 0xff);
646       val >>= 8;
647     }
648
649   return true;
650 }
651
652
653 //------------------------------------------------------------------------------------------
654 #ifdef KM_WIN32
655
656 #define TIMESTAMP_TO_SYSTIME(ts, t) \
657   (t)->wYear    = (ts).Year;   /* year */ \
658   (t)->wMonth   = (ts).Month;  /* month of year (1 - 12) */ \
659   (t)->wDay     = (ts).Day;    /* day of month (1 - 31) */ \
660   (t)->wHour    = (ts).Hour;   /* hours (0 - 23) */ \
661   (t)->wMinute  = (ts).Minute; /* minutes (0 - 59) */ \
662   (t)->wSecond  = (ts).Second; /* seconds (0 - 60) */ \
663   (t)->wDayOfWeek = 0; \
664   (t)->wMilliseconds = 0
665
666 #define SYSTIME_TO_TIMESTAMP(t, ts) \
667   (ts).Year   = (t)->wYear;    /* year */ \
668   (ts).Month  = (t)->wMonth;   /* month of year (1 - 12) */ \
669   (ts).Day    = (t)->wDay;     /* day of month (1 - 31) */ \
670   (ts).Hour   = (t)->wHour;    /* hours (0 - 23) */ \
671   (ts).Minute = (t)->wMinute;  /* minutes (0 - 59) */ \
672   (ts).Second = (t)->wSecond;  /* seconds (0 - 60) */
673
674 //
675 Kumu::Timestamp::Timestamp() :
676   Year(0), Month(0),  Day(0), Hour(0), Minute(0), Second(0)
677 {
678   SYSTEMTIME sys_time;
679   GetSystemTime(&sys_time);
680   SYSTIME_TO_TIMESTAMP(&sys_time, *this);
681 }
682
683 //
684 bool
685 Kumu::Timestamp::operator<(const Timestamp& rhs) const
686 {
687   SYSTEMTIME lhst, rhst;
688   FILETIME lft, rft;
689
690   TIMESTAMP_TO_SYSTIME(*this, &lhst);
691   TIMESTAMP_TO_SYSTIME(rhs, &rhst);
692   SystemTimeToFileTime(&lhst, &lft);
693   SystemTimeToFileTime(&rhst, &rft);
694   return ( CompareFileTime(&lft, &rft) == -1 );
695 }
696
697 //
698 bool
699 Kumu::Timestamp::operator>(const Timestamp& rhs) const
700 {
701   SYSTEMTIME lhst, rhst;
702   FILETIME lft, rft;
703
704   TIMESTAMP_TO_SYSTIME(*this, &lhst);
705   TIMESTAMP_TO_SYSTIME(rhs, &rhst);
706   SystemTimeToFileTime(&lhst, &lft);
707   SystemTimeToFileTime(&rhst, &rft);
708   return ( CompareFileTime(&lft, &rft) == 1 );
709 }
710
711 inline ui64_t
712 seconds_to_ns100(ui32_t seconds)
713 {
714   return ((ui64_t)seconds * 10000000);
715 }
716
717 //
718 void
719 Kumu::Timestamp::AddDays(i32_t days)
720 {
721   SYSTEMTIME current_st;
722   FILETIME current_ft;
723   ULARGE_INTEGER current_ul;
724
725   if ( days != 0 )
726     {
727       TIMESTAMP_TO_SYSTIME(*this, &current_st);
728       SystemTimeToFileTime(&current_st, &current_ft);
729       memcpy(&current_ul, &current_ft, sizeof(current_ul));
730       current_ul.QuadPart += ( seconds_to_ns100(86400) * (i64_t)days );
731       memcpy(&current_ft, &current_ul, sizeof(current_ft));
732       FileTimeToSystemTime(&current_ft, &current_st);
733       SYSTIME_TO_TIMESTAMP(&current_st, *this);
734     }
735 }
736
737 //
738 void
739 Kumu::Timestamp::AddHours(i32_t hours)
740 {
741   SYSTEMTIME current_st;
742   FILETIME current_ft;
743   ULARGE_INTEGER current_ul;
744
745   if ( hours != 0 )
746     {
747       TIMESTAMP_TO_SYSTIME(*this, &current_st);
748       SystemTimeToFileTime(&current_st, &current_ft);
749       memcpy(&current_ul, &current_ft, sizeof(current_ul));
750       current_ul.QuadPart += ( seconds_to_ns100(3600) * (i64_t)hours );
751       memcpy(&current_ft, &current_ul, sizeof(current_ft));
752       FileTimeToSystemTime(&current_ft, &current_st);
753       SYSTIME_TO_TIMESTAMP(&current_st, *this);
754     }
755 }
756
757 //
758 void
759 Kumu::Timestamp::AddMinutes(i32_t minutes)
760 {
761   SYSTEMTIME current_st;
762   FILETIME current_ft;
763   ULARGE_INTEGER current_ul;
764
765   if ( minutes != 0 )
766     {
767       TIMESTAMP_TO_SYSTIME(*this, &current_st);
768       SystemTimeToFileTime(&current_st, &current_ft);
769       memcpy(&current_ul, &current_ft, sizeof(current_ul));
770       current_ul.QuadPart += ( seconds_to_ns100(60) * (i64_t)minutes );
771       memcpy(&current_ft, &current_ul, sizeof(current_ft));
772       FileTimeToSystemTime(&current_ft, &current_st);
773       SYSTIME_TO_TIMESTAMP(&current_st, *this);
774     }
775 }
776
777 #else // KM_WIN32
778
779 #include <time.h>
780
781 #define TIMESTAMP_TO_CALTIME(ts, ct)                                   \
782   (ct)->date.year  = (ts).Year;    /* year */                          \
783   (ct)->date.month = (ts).Month;   /* month of year (1 - 12) */        \
784   (ct)->date.day   = (ts).Day;     /* day of month (1 - 31) */         \
785   (ct)->hour       = (ts).Hour;    /* hours (0 - 23) */                \
786   (ct)->minute     = (ts).Minute;  /* minutes (0 - 59) */              \
787   (ct)->second     = (ts).Second;  /* seconds (0 - 60) */              \
788   (ct)->offset     = 0;
789
790 #define CALTIME_TO_TIMESTAMP(ct, ts)                                   \
791   assert((ct)->offset == 0);                                           \
792   (ts).Year   = (ct)->date.year;    /* year */                         \
793   (ts).Month  = (ct)->date.month;   /* month of year (1 - 12) */       \
794   (ts).Day    = (ct)->date.day;     /* day of month (1 - 31) */        \
795   (ts).Hour   = (ct)->hour;         /* hours (0 - 23) */               \
796   (ts).Minute = (ct)->minute;       /* minutes (0 - 59) */             \
797   (ts).Second = (ct)->second;       /* seconds (0 - 60) */
798
799
800 //
801 Kumu::Timestamp::Timestamp() :
802   Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
803 {
804   Kumu::TAI::tai now;
805   Kumu::TAI::caltime ct;
806   now.now();
807   ct = now;
808   CALTIME_TO_TIMESTAMP(&ct, *this)
809 }
810
811 //
812 bool
813 Kumu::Timestamp::operator<(const Timestamp& rhs) const
814 {
815   Kumu::TAI::caltime lh_ct, rh_ct;
816   TIMESTAMP_TO_CALTIME(*this, &lh_ct)
817   TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
818
819   Kumu::TAI::tai lh_tai, rh_tai;
820   lh_tai = lh_ct;
821   rh_tai = rh_ct;
822
823   return ( lh_tai.x < rh_tai.x );
824 }
825
826 //
827 bool
828 Kumu::Timestamp::operator>(const Timestamp& rhs) const
829 {
830   Kumu::TAI::caltime lh_ct, rh_ct;
831   TIMESTAMP_TO_CALTIME(*this, &lh_ct)
832   TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
833
834   Kumu::TAI::tai lh_tai, rh_tai;
835   lh_tai = lh_ct;
836   rh_tai = rh_ct;
837
838   return ( lh_tai.x > rh_tai.x );
839 }
840
841 //
842 void
843 Kumu::Timestamp::AddDays(i32_t days)
844 {
845   Kumu::TAI::caltime ct;
846   Kumu::TAI::tai t;
847
848   if ( days != 0 )
849     {
850       TIMESTAMP_TO_CALTIME(*this, &ct)
851       t = ct;
852       t.add_days(days);
853       ct = t;
854       CALTIME_TO_TIMESTAMP(&ct, *this)
855     }
856 }
857
858 //
859 void
860 Kumu::Timestamp::AddHours(i32_t hours)
861 {
862   Kumu::TAI::caltime ct;
863   Kumu::TAI::tai t;
864
865   if ( hours != 0 )
866     {
867       TIMESTAMP_TO_CALTIME(*this, &ct)
868       t = ct;
869       t.add_hours(hours);
870       ct = t;
871       CALTIME_TO_TIMESTAMP(&ct, *this)
872     }
873 }
874
875 //
876 void
877 Kumu::Timestamp::AddMinutes(i32_t minutes)
878 {
879   Kumu::TAI::caltime ct;
880   Kumu::TAI::tai t;
881
882   if ( minutes != 0 )
883     {
884       TIMESTAMP_TO_CALTIME(*this, &ct)
885       t = ct;
886       t.add_minutes(minutes);
887       ct = t;
888       CALTIME_TO_TIMESTAMP(&ct, *this)
889     }
890 }
891
892 #endif // KM_WIN32
893
894
895 Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
896 {
897   Year   = rhs.Year;
898   Month  = rhs.Month;
899   Day    = rhs.Day;
900   Hour   = rhs.Hour;
901   Minute = rhs.Minute;
902   Second = rhs.Second;
903 }
904
905 Kumu::Timestamp::~Timestamp()
906 {
907 }
908
909 //
910 const Kumu::Timestamp&
911 Kumu::Timestamp::operator=(const Timestamp& rhs)
912 {
913   Year   = rhs.Year;
914   Month  = rhs.Month;
915   Day    = rhs.Day;
916   Hour   = rhs.Hour;
917   Minute = rhs.Minute;
918   Second = rhs.Second;
919   return *this;
920 }
921
922 //
923 bool
924 Kumu::Timestamp::operator==(const Timestamp& rhs) const
925 {
926   if ( Year == rhs.Year
927        && Month  == rhs.Month
928        && Day    == rhs.Day
929        && Hour   == rhs.Hour
930        && Minute == rhs.Minute
931        && Second == rhs.Second )
932     return true;
933
934   return false;
935 }
936
937 //
938 bool
939 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
940 {
941   if ( Year != rhs.Year
942        || Month  != rhs.Month
943        || Day    != rhs.Day
944        || Hour   != rhs.Hour
945        || Minute != rhs.Minute
946        || Second != rhs.Second )
947     return true;
948
949   return false;
950 }
951
952 // 
953 const char*
954 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
955 {
956   return EncodeStringWithOffset(str_buf, buf_len, 0);
957 }
958
959 // 
960 const char*
961 Kumu::Timestamp::EncodeStringWithOffset(char* str_buf, ui32_t buf_len,
962                                         i32_t offset_minutes) const
963 {
964   if ( buf_len < ( DateTimeLen + 1 ) )
965     return 0;
966
967   // ensure offset is within +/- 14 hours
968   if ((offset_minutes < -14 * 60) || (offset_minutes > 14 * 60))
969     return 0;
970
971   // set the apparent time
972   Kumu::Timestamp tmp_t(*this);
973   tmp_t.AddMinutes(offset_minutes);
974
975   char direction = '+';
976   if (offset_minutes < 0) {
977     direction = '-';
978     // need absolute offset from zero
979     offset_minutes = -offset_minutes;
980   }
981
982   // 2004-05-01T13:20:00+00:00
983   snprintf(str_buf, buf_len,
984            "%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02hu:%02hu",
985            tmp_t.Year, tmp_t.Month, tmp_t.Day,
986            tmp_t.Hour, tmp_t.Minute, tmp_t.Second,
987            direction,
988            offset_minutes / 60,
989            offset_minutes % 60);
990
991   return str_buf;
992 }
993
994 //
995 bool
996 Kumu::Timestamp::DecodeString(const char* datestr)
997 {
998   Timestamp TmpStamp;
999
1000   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
1001        || datestr[4] != '-'
1002        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
1003        || datestr[7] != '-'
1004        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
1005     return false;
1006
1007   ui32_t char_count = 10;
1008   TmpStamp.Year = atoi(datestr);
1009   TmpStamp.Month = atoi(datestr + 5);
1010   TmpStamp.Day = atoi(datestr + 8);
1011   TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
1012  
1013   if ( datestr[10] == 'T' )
1014     {
1015       if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
1016            || datestr[13] != ':'
1017            || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
1018         return false;
1019
1020       char_count += 6;
1021       TmpStamp.Hour = atoi(datestr + 11);
1022       TmpStamp.Minute = atoi(datestr + 14);
1023
1024       if ( datestr[16] == ':' )
1025         {
1026           if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
1027             return false;
1028
1029           char_count += 3;
1030           TmpStamp.Second = atoi(datestr + 17);
1031         }
1032
1033       if ( datestr[19] == '.' )
1034         {
1035           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
1036             return false;
1037           
1038           // we don't carry the ms value
1039           datestr += 4;
1040         }
1041
1042       if ( datestr[19] == '-' || datestr[19] == '+' )
1043         {
1044           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
1045                || datestr[22] != ':'
1046                || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
1047             return false;
1048
1049           char_count += 6;
1050
1051           ui32_t TZ_hh = atoi(datestr + 20);
1052           ui32_t TZ_mm = atoi(datestr + 23);
1053           if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
1054             return false;
1055
1056           i32_t TZ_offset = 60 * TZ_hh + TZ_mm;
1057           if (datestr[19] == '-')
1058             TZ_offset = -TZ_offset;
1059           /* at this point, TZ_offset reflects the contents of the string */
1060
1061           /* a negative offset is behind UTC and so needs to increment to
1062            * convert, while a positive offset must do the reverse */
1063           TmpStamp.AddMinutes(-TZ_offset);
1064         }
1065       else if (datestr[19] == 'Z')
1066         {
1067           /* act as if the offset were +00:00 */
1068           char_count++;
1069         }
1070     }
1071
1072   if ( datestr[char_count] != 0 )
1073     {
1074       Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
1075                                    datestr, char_count);
1076       return false;
1077     }
1078
1079 #ifdef KM_WIN32
1080   SYSTEMTIME st;
1081   FILETIME ft;
1082   TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
1083   if ( SystemTimeToFileTime(&st, &ft) == 0 )
1084     return false;
1085   SYSTIME_TO_TIMESTAMP(&st, *this);
1086 #else
1087   Kumu::TAI::tai t;
1088   Kumu::TAI::caltime ct;
1089   TIMESTAMP_TO_CALTIME(TmpStamp, &ct);
1090   t = ct; // back and forth to tai to normalize offset
1091   ct = t;
1092   CALTIME_TO_TIMESTAMP(&ct, *this)
1093 #endif
1094
1095   return true;
1096 }
1097
1098 //
1099 bool
1100 Kumu::Timestamp::HasValue() const
1101 {
1102   if ( Year || Month || Day || Hour || Minute || Second )
1103     return true;
1104
1105   return false;
1106 }
1107
1108 //
1109 bool
1110 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
1111 {
1112   assert(Reader);
1113   if ( ! Reader->ReadUi16BE(&Year) ) return false;      
1114   if ( ! Reader->ReadRaw(&Month, 6) ) return false;
1115   return true;
1116 }
1117
1118 //
1119 bool
1120 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
1121 {
1122   assert(Writer);
1123   if ( ! Writer->WriteUi16BE(Year) ) return false;      
1124   if ( ! Writer->WriteRaw(&Month, 6) ) return false;
1125   return true;
1126 }
1127
1128 //------------------------------------------------------------------------------------------
1129
1130 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
1131   : m_p(0), m_capacity(0), m_size(0)
1132 {
1133   m_p = Buf->Data();
1134   m_capacity = Buf->Capacity();
1135   assert(m_p); assert(m_capacity);
1136 }
1137
1138 bool
1139 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
1140 {
1141   if ( ( m_size + ber_len ) > m_capacity )
1142     return false;
1143
1144   if ( ! write_BER(m_p + m_size, i, ber_len) )
1145     return false;
1146
1147   m_size += ber_len;
1148   return true;
1149 }
1150
1151
1152 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1153   : m_p(0), m_capacity(0), m_size(0)
1154 {
1155   m_p = Buf->RoData();
1156   m_capacity = Buf->Length();
1157   assert(m_p); assert(m_capacity);
1158 }
1159
1160 bool
1161 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1162 {
1163   if ( i == 0 || ber_len == 0 ) return false;
1164
1165   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1166     return false;
1167
1168   if ( ( m_size + *ber_len ) > m_capacity )
1169     return false;
1170
1171   if ( ! read_BER(m_p + m_size, i) )
1172     return false;
1173
1174   m_size += *ber_len;
1175   return true;
1176 }
1177
1178 //------------------------------------------------------------------------------------------
1179
1180 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1181
1182 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1183 {
1184   Capacity(cap);
1185 }
1186
1187 Kumu::ByteString::~ByteString()
1188 {
1189   if ( m_Data != 0 )
1190     free(m_Data);
1191 }
1192
1193
1194 // copy the given data into the ByteString, set Length value.
1195 // Returns error if the ByteString is too small.
1196 Kumu::Result_t
1197 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1198 {
1199   if ( m_Capacity < buf_len )
1200     return RESULT_ALLOC;
1201
1202   memcpy(m_Data, buf, buf_len);
1203   m_Length = buf_len;
1204   return RESULT_OK;
1205 }
1206
1207
1208 // copy the given data into the ByteString, set Length value.
1209 // Returns error if the ByteString is too small.
1210 Kumu::Result_t
1211 Kumu::ByteString::Set(const ByteString& Buf)
1212 {
1213   if ( m_Capacity < Buf.m_Capacity )
1214     return RESULT_ALLOC;
1215
1216   memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1217   m_Length = Buf.m_Length;
1218   return RESULT_OK;
1219 }
1220
1221
1222 // Sets the size of the internally allocate buffer.
1223 Kumu::Result_t
1224 Kumu::ByteString::Capacity(ui32_t cap_size)
1225 {
1226   if ( m_Capacity >= cap_size )
1227     return RESULT_OK;
1228
1229   byte_t* tmp_data = 0;
1230   if ( m_Data != 0 )
1231     {
1232       if ( m_Length > 0 )
1233         tmp_data = m_Data;
1234       else
1235         free(m_Data);
1236     }
1237                 
1238   if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1239     return RESULT_ALLOC;
1240
1241   if ( tmp_data != 0 )
1242     {
1243       assert(m_Length > 0);
1244       memcpy(m_Data, tmp_data, m_Length);
1245       free(tmp_data);
1246     }
1247                 
1248   m_Capacity = cap_size;
1249   return RESULT_OK;
1250 }
1251
1252 //
1253 Kumu::Result_t
1254 Kumu::ByteString::Append(const ByteString& Buf)
1255 {
1256   Result_t result = RESULT_OK;
1257   ui32_t diff = m_Capacity - m_Length;
1258
1259   if ( diff < Buf.Length() )
1260     result = Capacity(m_Capacity + Buf.Length());
1261
1262   if ( KM_SUCCESS(result) )
1263     {
1264       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1265       m_Length += Buf.Length();
1266     }
1267
1268   return result;
1269 }
1270
1271 //
1272 Kumu::Result_t
1273 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1274 {
1275   Result_t result = RESULT_OK;
1276   ui32_t diff = m_Capacity - m_Length;
1277
1278   if ( diff < buf_len )
1279     result = Capacity(m_Capacity + buf_len);
1280
1281   if ( KM_SUCCESS(result) )
1282     {
1283       memcpy(m_Data + m_Length, buf, buf_len);
1284       m_Length += buf_len;
1285     }
1286
1287   return result;
1288 }
1289
1290
1291 //
1292 // end KM_util.cpp
1293 //