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