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