Allow for build-time configuration of randomly cased UUIDs.
[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) * (ui64_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) * (ui64_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 #else // KM_WIN32
758
759 #include <time.h>
760
761 #define TIMESTAMP_TO_CALTIME(ts, ct)                                   \
762   (ct)->date.year  = (ts).Year;    /* year */                          \
763   (ct)->date.month = (ts).Month;   /* month of year (1 - 12) */        \
764   (ct)->date.day   = (ts).Day;     /* day of month (1 - 31) */         \
765   (ct)->hour       = (ts).Hour;    /* hours (0 - 23) */                \
766   (ct)->minute     = (ts).Minute;  /* minutes (0 - 59) */              \
767   (ct)->second     = (ts).Second;  /* seconds (0 - 60) */              \
768   (ct)->offset     = 0;
769
770 #define CALTIME_TO_TIMESTAMP(ct, ts)                                   \
771   assert((ct)->offset == 0);                                           \
772   (ts).Year   = (ct)->date.year;    /* year */                         \
773   (ts).Month  = (ct)->date.month;   /* month of year (1 - 12) */       \
774   (ts).Day    = (ct)->date.day;     /* day of month (1 - 31) */        \
775   (ts).Hour   = (ct)->hour;         /* hours (0 - 23) */               \
776   (ts).Minute = (ct)->minute;       /* minutes (0 - 59) */             \
777   (ts).Second = (ct)->second;       /* seconds (0 - 60) */
778
779
780 //
781 Kumu::Timestamp::Timestamp() :
782   Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
783 {
784   Kumu::TAI::tai now;
785   Kumu::TAI::caltime ct;
786   now.now();
787   ct = now;
788   CALTIME_TO_TIMESTAMP(&ct, *this)
789 }
790
791 //
792 bool
793 Kumu::Timestamp::operator<(const Timestamp& rhs) const
794 {
795   Kumu::TAI::caltime lh_ct, rh_ct;
796   TIMESTAMP_TO_CALTIME(*this, &lh_ct)
797   TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
798
799   Kumu::TAI::tai lh_tai, rh_tai;
800   lh_tai = lh_ct;
801   rh_tai = rh_ct;
802
803   return ( lh_tai.x < rh_tai.x );
804 }
805
806 //
807 bool
808 Kumu::Timestamp::operator>(const Timestamp& rhs) const
809 {
810   Kumu::TAI::caltime lh_ct, rh_ct;
811   TIMESTAMP_TO_CALTIME(*this, &lh_ct)
812   TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
813
814   Kumu::TAI::tai lh_tai, rh_tai;
815   lh_tai = lh_ct;
816   rh_tai = rh_ct;
817
818   return ( lh_tai.x > rh_tai.x );
819 }
820
821 //
822 void
823 Kumu::Timestamp::AddDays(i32_t days)
824 {
825   Kumu::TAI::caltime ct;
826   Kumu::TAI::tai t;
827
828   if ( days != 0 )
829     {
830       TIMESTAMP_TO_CALTIME(*this, &ct)
831       t = ct;
832       t.add_days(days);
833       ct = t;
834       CALTIME_TO_TIMESTAMP(&ct, *this)
835     }
836 }
837
838 //
839 void
840 Kumu::Timestamp::AddHours(i32_t hours)
841 {
842   Kumu::TAI::caltime ct;
843   Kumu::TAI::tai t;
844
845   if ( hours != 0 )
846     {
847       TIMESTAMP_TO_CALTIME(*this, &ct)
848       t = ct;
849       t.add_hours(hours);
850       ct = t;
851       CALTIME_TO_TIMESTAMP(&ct, *this)
852     }
853 }
854
855 #endif // KM_WIN32
856
857
858 Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
859 {
860   Year   = rhs.Year;
861   Month  = rhs.Month;
862   Day    = rhs.Day;
863   Hour   = rhs.Hour;
864   Minute = rhs.Minute;
865   Second = rhs.Second;
866 }
867
868 Kumu::Timestamp::~Timestamp()
869 {
870 }
871
872 //
873 const Kumu::Timestamp&
874 Kumu::Timestamp::operator=(const Timestamp& rhs)
875 {
876   Year   = rhs.Year;
877   Month  = rhs.Month;
878   Day    = rhs.Day;
879   Hour   = rhs.Hour;
880   Minute = rhs.Minute;
881   Second = rhs.Second;
882   return *this;
883 }
884
885 //
886 bool
887 Kumu::Timestamp::operator==(const Timestamp& rhs) const
888 {
889   if ( Year == rhs.Year
890        && Month  == rhs.Month
891        && Day    == rhs.Day
892        && Hour   == rhs.Hour
893        && Minute == rhs.Minute
894        && Second == rhs.Second )
895     return true;
896
897   return false;
898 }
899
900 //
901 bool
902 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
903 {
904   if ( Year != rhs.Year
905        || Month  != rhs.Month
906        || Day    != rhs.Day
907        || Hour   != rhs.Hour
908        || Minute != rhs.Minute
909        || Second != rhs.Second )
910     return true;
911
912   return false;
913 }
914
915 // 
916 const char*
917 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
918 {
919   if ( buf_len < ( DateTimeLen + 1 ) )
920     return 0;
921
922   // 2004-05-01T13:20:00-00:00
923   snprintf(str_buf, buf_len,
924            "%04hu-%02hu-%02huT%02hu:%02hu:%02hu+00:00",
925            Year, Month, Day, Hour, Minute, Second);
926   
927   return str_buf;
928 }
929
930 //
931 bool
932 Kumu::Timestamp::DecodeString(const char* datestr)
933 {
934   Timestamp TmpStamp;
935
936   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
937        || datestr[4] != '-'
938        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
939        || datestr[7] != '-'
940        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
941     return false;
942
943   ui32_t char_count = 10;
944   TmpStamp.Year = atoi(datestr);
945   TmpStamp.Month = atoi(datestr + 5);
946   TmpStamp.Day = atoi(datestr + 8);
947   TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
948  
949   if ( datestr[10] == 'T' )
950     {
951       if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
952            || datestr[13] != ':'
953            || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
954         return false;
955
956       char_count += 6;
957       TmpStamp.Hour = atoi(datestr + 11);
958       TmpStamp.Minute = atoi(datestr + 14);
959
960       if ( datestr[16] == ':' )
961         {
962           if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
963             return false;
964
965           char_count += 3;
966           TmpStamp.Second = atoi(datestr + 17);
967         }
968
969       if ( datestr[19] == '.' )
970         {
971           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
972             return false;
973           
974           // we don't carry the ms value
975           datestr += 4;
976         }
977
978       if ( datestr[19] == '-' || datestr[19] == '+' )
979         {
980           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
981                || datestr[22] != ':'
982                || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
983             return false;
984
985           char_count += 6;
986           ui32_t TZ_hh = atoi(datestr + 20);
987           ui32_t TZ_mm = atoi(datestr + 23);
988       
989           if ( TZ_mm != 0 )
990             Kumu::DefaultLogSink().Warn("Ignoring minutes in timezone offset: %u\n", TZ_mm);
991           
992           if ( TZ_hh > 12 )
993             return false;
994
995           else 
996             AddHours( (datestr[19] == '-' ? (0 - TZ_hh) : TZ_hh));
997         }
998     }
999
1000   if ( datestr[char_count] != 0 )
1001     {
1002       Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
1003                                    datestr, char_count);
1004       return false;
1005     }
1006
1007 #ifdef KM_WIN32
1008   SYSTEMTIME st;
1009   FILETIME ft;
1010   TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
1011   if ( SystemTimeToFileTime(&st, &ft) == 0 )
1012     return false;
1013   SYSTIME_TO_TIMESTAMP(&st, *this);
1014 #else
1015   Kumu::TAI::tai t;
1016   Kumu::TAI::caltime ct;
1017   TIMESTAMP_TO_CALTIME(TmpStamp, &ct);
1018   t = ct; // back and forth to tai to normalize offset
1019   ct = t;
1020   CALTIME_TO_TIMESTAMP(&ct, *this)
1021 #endif
1022
1023   return true;
1024 }
1025
1026 //
1027 bool
1028 Kumu::Timestamp::HasValue() const
1029 {
1030   if ( Year || Month || Day || Hour || Minute || Second )
1031     return true;
1032
1033   return false;
1034 }
1035
1036 //
1037 bool
1038 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
1039 {
1040   assert(Reader);
1041   if ( ! Reader->ReadUi16BE(&Year) ) return false;      
1042   if ( ! Reader->ReadRaw(&Month, 6) ) return false;
1043   return true;
1044 }
1045
1046 //
1047 bool
1048 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
1049 {
1050   assert(Writer);
1051   if ( ! Writer->WriteUi16BE(Year) ) return false;      
1052   if ( ! Writer->WriteRaw(&Month, 6) ) return false;
1053   return true;
1054 }
1055
1056 //------------------------------------------------------------------------------------------
1057
1058 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
1059   : m_p(0), m_capacity(0), m_size(0)
1060 {
1061   m_p = Buf->Data();
1062   m_capacity = Buf->Capacity();
1063   assert(m_p); assert(m_capacity);
1064 }
1065
1066 bool
1067 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
1068 {
1069   if ( ( m_size + ber_len ) > m_capacity )
1070     return false;
1071
1072   if ( ! write_BER(m_p + m_size, i, ber_len) )
1073     return false;
1074
1075   m_size += ber_len;
1076   return true;
1077 }
1078
1079
1080 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1081   : m_p(0), m_capacity(0), m_size(0)
1082 {
1083   m_p = Buf->RoData();
1084   m_capacity = Buf->Length();
1085   assert(m_p); assert(m_capacity);
1086 }
1087
1088 bool
1089 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1090 {
1091   if ( i == 0 || ber_len == 0 ) return false;
1092
1093   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1094     return false;
1095
1096   if ( ( m_size + *ber_len ) > m_capacity )
1097     return false;
1098
1099   if ( ! read_BER(m_p + m_size, i) )
1100     return false;
1101
1102   m_size += *ber_len;
1103   return true;
1104 }
1105
1106 //------------------------------------------------------------------------------------------
1107
1108 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1109
1110 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1111 {
1112   Capacity(cap);
1113 }
1114
1115 Kumu::ByteString::~ByteString()
1116 {
1117   if ( m_Data != 0 )
1118     free(m_Data);
1119 }
1120
1121
1122 // copy the given data into the ByteString, set Length value.
1123 // Returns error if the ByteString is too small.
1124 Kumu::Result_t
1125 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1126 {
1127   if ( m_Capacity < buf_len )
1128     return RESULT_ALLOC;
1129
1130   memcpy(m_Data, buf, buf_len);
1131   m_Length = buf_len;
1132   return RESULT_OK;
1133 }
1134
1135
1136 // copy the given data into the ByteString, set Length value.
1137 // Returns error if the ByteString is too small.
1138 Kumu::Result_t
1139 Kumu::ByteString::Set(const ByteString& Buf)
1140 {
1141   if ( m_Capacity < Buf.m_Capacity )
1142     return RESULT_ALLOC;
1143
1144   memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1145   m_Length = Buf.m_Length;
1146   return RESULT_OK;
1147 }
1148
1149
1150 // Sets the size of the internally allocate buffer.
1151 Kumu::Result_t
1152 Kumu::ByteString::Capacity(ui32_t cap_size)
1153 {
1154   if ( m_Capacity >= cap_size )
1155     return RESULT_OK;
1156
1157   byte_t* tmp_data = 0;
1158   if ( m_Data != 0 )
1159     {
1160       if ( m_Length > 0 )
1161         tmp_data = m_Data;
1162       else
1163         free(m_Data);
1164     }
1165                 
1166   if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1167     return RESULT_ALLOC;
1168
1169   if ( tmp_data != 0 )
1170     {
1171       assert(m_Length > 0);
1172       memcpy(m_Data, tmp_data, m_Length);
1173       free(tmp_data);
1174     }
1175                 
1176   m_Capacity = cap_size;
1177   return RESULT_OK;
1178 }
1179
1180 //
1181 Kumu::Result_t
1182 Kumu::ByteString::Append(const ByteString& Buf)
1183 {
1184   Result_t result = RESULT_OK;
1185   ui32_t diff = m_Capacity - m_Length;
1186
1187   if ( diff < Buf.Length() )
1188     result = Capacity(m_Capacity + Buf.Length());
1189
1190   if ( KM_SUCCESS(result) )
1191     {
1192       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1193       m_Length += Buf.Length();
1194     }
1195
1196   return result;
1197 }
1198
1199 //
1200 Kumu::Result_t
1201 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1202 {
1203   Result_t result = RESULT_OK;
1204   ui32_t diff = m_Capacity - m_Length;
1205
1206   if ( diff < buf_len )
1207     result = Capacity(m_Capacity + buf_len);
1208
1209   if ( KM_SUCCESS(result) )
1210     {
1211       memcpy(m_Data + m_Length, buf, buf_len);
1212       m_Length += buf_len;
1213     }
1214
1215   return result;
1216 }
1217
1218
1219 //
1220 // end KM_util.cpp
1221 //