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