const, baby
[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 bool
544 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
545 {
546   if ( buf == 0 )
547     return false;
548
549   if ( ( **buf & 0x80 ) == 0 )
550     return false;
551
552   ui64_t val = 0;
553   ui8_t ber_size = ( **buf & 0x0f ) + 1;
554
555   if ( ber_size > 9 )
556     return false;
557
558   for ( ui8_t i = 1; i < ber_size; i++ )
559     {
560       if ( (*buf)[i] > 0 )
561         val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
562     }
563
564   *buf += ber_size;
565   return ( val == test_value );
566 }
567
568
569 //
570 bool
571 Kumu::read_BER(const byte_t* buf, ui64_t* val)
572 {
573   ui8_t ber_size, i;
574   
575   if ( buf == 0 || val == 0 )
576     return false;
577
578   if ( ( *buf & 0x80 ) == 0 )
579     return false;
580
581   *val = 0;
582   ber_size = ( *buf & 0x0f ) + 1;
583
584   if ( ber_size > 9 )
585     return false;
586
587   for ( i = 1; i < ber_size; i++ )
588     {
589       if ( buf[i] > 0 )
590         *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
591     }
592
593   return true;
594 }
595
596
597 static const ui64_t ber_masks[9] =
598   { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), 
599     ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
600     ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
601     ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
602     0
603   };
604
605 //
606 ui32_t
607 Kumu::get_BER_length_for_value(ui64_t val)
608 {
609   for ( ui32_t i = 0; i < 9; i++ )
610     {
611       if ( ( val & ber_masks[i] ) == 0 )
612         return i + 1;
613     }
614
615   ui64Printer tmp_i(val);
616   DefaultLogSink().Error("BER integer encoding not supported for large value %s\n", tmp_i.c_str());
617   return 0;
618 }
619
620 //
621 bool
622 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
623 {
624   if ( buf == 0 )
625     return false;
626
627   if ( ber_len == 0 )
628     { // calculate default length
629       if ( val < 0x01000000L )
630         ber_len = 4;
631       else if ( val < ui64_C(0x0100000000000000) )
632         ber_len = 8;
633       else
634         ber_len = 9;
635     }
636   else
637     { // sanity check BER length
638       if ( ber_len > 9 )
639         {
640           DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len);
641           return false;
642         }
643       
644       if ( ( val & ber_masks[ber_len - 1] ) != 0 )
645         {
646           ui64Printer tmp_i(val);
647           DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str());
648           return false;
649         }
650     }
651
652   buf[0] = 0x80 + ( ber_len - 1 );
653
654   for ( ui32_t i = ber_len - 1; i > 0; i-- )
655     {
656       buf[i] = (ui8_t)(val & 0xff);
657       val >>= 8;
658     }
659
660   return true;
661 }
662
663
664 //------------------------------------------------------------------------------------------
665 #ifdef KM_WIN32
666
667 #define TIMESTAMP_TO_SYSTIME(ts, t) \
668   (t)->wYear    = (ts).Year;   /* year */ \
669   (t)->wMonth   = (ts).Month;  /* month of year (1 - 12) */ \
670   (t)->wDay     = (ts).Day;    /* day of month (1 - 31) */ \
671   (t)->wHour    = (ts).Hour;   /* hours (0 - 23) */ \
672   (t)->wMinute  = (ts).Minute; /* minutes (0 - 59) */ \
673   (t)->wSecond  = (ts).Second; /* seconds (0 - 60) */ \
674   (t)->wDayOfWeek = 0; \
675   (t)->wMilliseconds = 0
676
677 #define SYSTIME_TO_TIMESTAMP(t, ts) \
678   (ts).Year   = (t)->wYear;    /* year */ \
679   (ts).Month  = (t)->wMonth;   /* month of year (1 - 12) */ \
680   (ts).Day    = (t)->wDay;     /* day of month (1 - 31) */ \
681   (ts).Hour   = (t)->wHour;    /* hours (0 - 23) */ \
682   (ts).Minute = (t)->wMinute;  /* minutes (0 - 59) */ \
683   (ts).Second = (t)->wSecond;  /* seconds (0 - 60) */
684
685 //
686 Kumu::Timestamp::Timestamp() :
687   Year(0), Month(0),  Day(0), Hour(0), Minute(0), Second(0)
688 {
689   SYSTEMTIME sys_time;
690   GetSystemTime(&sys_time);
691   SYSTIME_TO_TIMESTAMP(&sys_time, *this);
692 }
693
694 //
695 bool
696 Kumu::Timestamp::operator<(const Timestamp& rhs) const
697 {
698   SYSTEMTIME lhst, rhst;
699   FILETIME lft, rft;
700
701   TIMESTAMP_TO_SYSTIME(*this, &lhst);
702   TIMESTAMP_TO_SYSTIME(rhs, &rhst);
703   SystemTimeToFileTime(&lhst, &lft);
704   SystemTimeToFileTime(&rhst, &rft);
705   return ( CompareFileTime(&lft, &rft) == -1 );
706 }
707
708 //
709 bool
710 Kumu::Timestamp::operator>(const Timestamp& rhs) const
711 {
712   SYSTEMTIME lhst, rhst;
713   FILETIME lft, rft;
714
715   TIMESTAMP_TO_SYSTIME(*this, &lhst);
716   TIMESTAMP_TO_SYSTIME(rhs, &rhst);
717   SystemTimeToFileTime(&lhst, &lft);
718   SystemTimeToFileTime(&rhst, &rft);
719   return ( CompareFileTime(&lft, &rft) == 1 );
720 }
721
722 inline ui64_t
723 seconds_to_ns100(ui32_t seconds)
724 {
725   return ((ui64_t)seconds * 10000000);
726 }
727
728 //
729 void
730 Kumu::Timestamp::AddDays(i32_t days)
731 {
732   SYSTEMTIME current_st;
733   FILETIME current_ft;
734   ULARGE_INTEGER current_ul;
735
736   if ( days != 0 )
737     {
738       TIMESTAMP_TO_SYSTIME(*this, &current_st);
739       SystemTimeToFileTime(&current_st, &current_ft);
740       memcpy(&current_ul, &current_ft, sizeof(current_ul));
741       current_ul.QuadPart += ( seconds_to_ns100(86400) * (i64_t)days );
742       memcpy(&current_ft, &current_ul, sizeof(current_ft));
743       FileTimeToSystemTime(&current_ft, &current_st);
744       SYSTIME_TO_TIMESTAMP(&current_st, *this);
745     }
746 }
747
748 //
749 void
750 Kumu::Timestamp::AddHours(i32_t hours)
751 {
752   SYSTEMTIME current_st;
753   FILETIME current_ft;
754   ULARGE_INTEGER current_ul;
755
756   if ( hours != 0 )
757     {
758       TIMESTAMP_TO_SYSTIME(*this, &current_st);
759       SystemTimeToFileTime(&current_st, &current_ft);
760       memcpy(&current_ul, &current_ft, sizeof(current_ul));
761       current_ul.QuadPart += ( seconds_to_ns100(3600) * (i64_t)hours );
762       memcpy(&current_ft, &current_ul, sizeof(current_ft));
763       FileTimeToSystemTime(&current_ft, &current_st);
764       SYSTIME_TO_TIMESTAMP(&current_st, *this);
765     }
766 }
767
768 //
769 void
770 Kumu::Timestamp::AddMinutes(i32_t minutes)
771 {
772   SYSTEMTIME current_st;
773   FILETIME current_ft;
774   ULARGE_INTEGER current_ul;
775
776   if ( minutes != 0 )
777     {
778       TIMESTAMP_TO_SYSTIME(*this, &current_st);
779       SystemTimeToFileTime(&current_st, &current_ft);
780       memcpy(&current_ul, &current_ft, sizeof(current_ul));
781       current_ul.QuadPart += ( seconds_to_ns100(60) * (i64_t)minutes );
782       memcpy(&current_ft, &current_ul, sizeof(current_ft));
783       FileTimeToSystemTime(&current_ft, &current_st);
784       SYSTIME_TO_TIMESTAMP(&current_st, *this);
785     }
786 }
787
788 //
789 void
790 Kumu::Timestamp::AddSeconds(i32_t seconds)
791 {
792   SYSTEMTIME current_st;
793   FILETIME current_ft;
794   ULARGE_INTEGER current_ul;
795
796   if ( seconds != 0 )
797     {
798       TIMESTAMP_TO_SYSTIME(*this, &current_st);
799       SystemTimeToFileTime(&current_st, &current_ft);
800       memcpy(&current_ul, &current_ft, sizeof(current_ul));
801       current_ul.QuadPart += ( seconds_to_ns100(1) * (i64_t)seconds );
802       memcpy(&current_ft, &current_ul, sizeof(current_ft));
803       FileTimeToSystemTime(&current_ft, &current_st);
804       SYSTIME_TO_TIMESTAMP(&current_st, *this);
805     }
806 }
807
808 #else // KM_WIN32
809
810 #include <time.h>
811
812 #define TIMESTAMP_TO_CALTIME(ts, ct)                                   \
813   (ct)->date.year  = (ts).Year;    /* year */                          \
814   (ct)->date.month = (ts).Month;   /* month of year (1 - 12) */        \
815   (ct)->date.day   = (ts).Day;     /* day of month (1 - 31) */         \
816   (ct)->hour       = (ts).Hour;    /* hours (0 - 23) */                \
817   (ct)->minute     = (ts).Minute;  /* minutes (0 - 59) */              \
818   (ct)->second     = (ts).Second;  /* seconds (0 - 60) */              \
819   (ct)->offset     = 0;
820
821 #define CALTIME_TO_TIMESTAMP(ct, ts)                                   \
822   assert((ct)->offset == 0);                                           \
823   (ts).Year   = (ct)->date.year;    /* year */                         \
824   (ts).Month  = (ct)->date.month;   /* month of year (1 - 12) */       \
825   (ts).Day    = (ct)->date.day;     /* day of month (1 - 31) */        \
826   (ts).Hour   = (ct)->hour;         /* hours (0 - 23) */               \
827   (ts).Minute = (ct)->minute;       /* minutes (0 - 59) */             \
828   (ts).Second = (ct)->second;       /* seconds (0 - 60) */
829
830
831 //
832 Kumu::Timestamp::Timestamp() :
833   Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
834 {
835   Kumu::TAI::tai now;
836   Kumu::TAI::caltime ct;
837   now.now();
838   ct = now;
839   CALTIME_TO_TIMESTAMP(&ct, *this)
840 }
841
842 //
843 bool
844 Kumu::Timestamp::operator<(const Timestamp& rhs) const
845 {
846   Kumu::TAI::caltime lh_ct, rh_ct;
847   TIMESTAMP_TO_CALTIME(*this, &lh_ct)
848   TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
849
850   Kumu::TAI::tai lh_tai, rh_tai;
851   lh_tai = lh_ct;
852   rh_tai = rh_ct;
853
854   return ( lh_tai.x < rh_tai.x );
855 }
856
857 //
858 bool
859 Kumu::Timestamp::operator>(const Timestamp& rhs) const
860 {
861   Kumu::TAI::caltime lh_ct, rh_ct;
862   TIMESTAMP_TO_CALTIME(*this, &lh_ct)
863   TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
864
865   Kumu::TAI::tai lh_tai, rh_tai;
866   lh_tai = lh_ct;
867   rh_tai = rh_ct;
868
869   return ( lh_tai.x > rh_tai.x );
870 }
871
872 //
873 void
874 Kumu::Timestamp::AddDays(i32_t days)
875 {
876   Kumu::TAI::caltime ct;
877   Kumu::TAI::tai t;
878
879   if ( days != 0 )
880     {
881       TIMESTAMP_TO_CALTIME(*this, &ct)
882       t = ct;
883       t.add_days(days);
884       ct = t;
885       CALTIME_TO_TIMESTAMP(&ct, *this)
886     }
887 }
888
889 //
890 void
891 Kumu::Timestamp::AddHours(i32_t hours)
892 {
893   Kumu::TAI::caltime ct;
894   Kumu::TAI::tai t;
895
896   if ( hours != 0 )
897     {
898       TIMESTAMP_TO_CALTIME(*this, &ct)
899       t = ct;
900       t.add_hours(hours);
901       ct = t;
902       CALTIME_TO_TIMESTAMP(&ct, *this)
903     }
904 }
905
906 //
907 void
908 Kumu::Timestamp::AddMinutes(i32_t minutes)
909 {
910   Kumu::TAI::caltime ct;
911   Kumu::TAI::tai t;
912
913   if ( minutes != 0 )
914     {
915       TIMESTAMP_TO_CALTIME(*this, &ct)
916       t = ct;
917       t.add_minutes(minutes);
918       ct = t;
919       CALTIME_TO_TIMESTAMP(&ct, *this)
920     }
921 }
922
923 //
924 void
925 Kumu::Timestamp::AddSeconds(i32_t seconds)
926 {
927   Kumu::TAI::caltime ct;
928   Kumu::TAI::tai t;
929
930   if ( seconds != 0 )
931     {
932       TIMESTAMP_TO_CALTIME(*this, &ct)
933       t = ct;
934       t.add_seconds(seconds);
935       ct = t;
936       CALTIME_TO_TIMESTAMP(&ct, *this)
937     }
938 }
939
940 #endif // KM_WIN32
941
942
943 Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
944 {
945   Year   = rhs.Year;
946   Month  = rhs.Month;
947   Day    = rhs.Day;
948   Hour   = rhs.Hour;
949   Minute = rhs.Minute;
950   Second = rhs.Second;
951 }
952
953 Kumu::Timestamp::~Timestamp()
954 {
955 }
956
957 //
958 const Kumu::Timestamp&
959 Kumu::Timestamp::operator=(const Timestamp& rhs)
960 {
961   Year   = rhs.Year;
962   Month  = rhs.Month;
963   Day    = rhs.Day;
964   Hour   = rhs.Hour;
965   Minute = rhs.Minute;
966   Second = rhs.Second;
967   return *this;
968 }
969
970 //
971 bool
972 Kumu::Timestamp::operator==(const Timestamp& rhs) const
973 {
974   if ( Year == rhs.Year
975        && Month  == rhs.Month
976        && Day    == rhs.Day
977        && Hour   == rhs.Hour
978        && Minute == rhs.Minute
979        && Second == rhs.Second )
980     return true;
981
982   return false;
983 }
984
985 //
986 bool
987 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
988 {
989   if ( Year != rhs.Year
990        || Month  != rhs.Month
991        || Day    != rhs.Day
992        || Hour   != rhs.Hour
993        || Minute != rhs.Minute
994        || Second != rhs.Second )
995     return true;
996
997   return false;
998 }
999
1000 // 
1001 const char*
1002 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
1003 {
1004   return EncodeStringWithOffset(str_buf, buf_len, 0);
1005 }
1006
1007 // 
1008 const char*
1009 Kumu::Timestamp::EncodeStringWithOffset(char* str_buf, ui32_t buf_len,
1010                                         i32_t offset_minutes) const
1011 {
1012   if ( buf_len < ( DateTimeLen + 1 ) )
1013     return 0;
1014
1015   // ensure offset is within +/- 14 hours
1016   if ((offset_minutes < -14 * 60) || (offset_minutes > 14 * 60))
1017     return 0;
1018
1019   // set the apparent time
1020   Kumu::Timestamp tmp_t(*this);
1021   tmp_t.AddMinutes(offset_minutes);
1022
1023   char direction = '+';
1024   if (offset_minutes < 0) {
1025     direction = '-';
1026     // need absolute offset from zero
1027     offset_minutes = -offset_minutes;
1028   }
1029
1030   // 2004-05-01T13:20:00+00:00
1031   snprintf(str_buf, buf_len,
1032            "%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02hu:%02hu",
1033            tmp_t.Year, tmp_t.Month, tmp_t.Day,
1034            tmp_t.Hour, tmp_t.Minute, tmp_t.Second,
1035            direction,
1036            offset_minutes / 60,
1037            offset_minutes % 60);
1038
1039   return str_buf;
1040 }
1041
1042 //
1043 bool
1044 Kumu::Timestamp::DecodeString(const char* datestr)
1045 {
1046   Timestamp TmpStamp;
1047
1048   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
1049        || datestr[4] != '-'
1050        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
1051        || datestr[7] != '-'
1052        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
1053     return false;
1054
1055   ui32_t char_count = 10;
1056   TmpStamp.Year = atoi(datestr);
1057   TmpStamp.Month = atoi(datestr + 5);
1058   TmpStamp.Day = atoi(datestr + 8);
1059   TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
1060  
1061   if ( datestr[10] == 'T' )
1062     {
1063       if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
1064            || datestr[13] != ':'
1065            || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
1066         return false;
1067
1068       char_count += 6;
1069       TmpStamp.Hour = atoi(datestr + 11);
1070       TmpStamp.Minute = atoi(datestr + 14);
1071
1072       if ( datestr[16] == ':' )
1073         {
1074           if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
1075             return false;
1076
1077           char_count += 3;
1078           TmpStamp.Second = atoi(datestr + 17);
1079         }
1080
1081       if ( datestr[19] == '.' )
1082         {
1083           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
1084             return false;
1085           
1086           // we don't carry the ms value
1087           datestr += 4;
1088         }
1089
1090       if ( datestr[19] == '-' || datestr[19] == '+' )
1091         {
1092           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
1093                || datestr[22] != ':'
1094                || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
1095             return false;
1096
1097           char_count += 6;
1098
1099           ui32_t TZ_hh = atoi(datestr + 20);
1100           ui32_t TZ_mm = atoi(datestr + 23);
1101           if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
1102             return false;
1103
1104           i32_t TZ_offset = 60 * TZ_hh + TZ_mm;
1105           if (datestr[19] == '-')
1106             TZ_offset = -TZ_offset;
1107           /* at this point, TZ_offset reflects the contents of the string */
1108
1109           /* a negative offset is behind UTC and so needs to increment to
1110            * convert, while a positive offset must do the reverse */
1111           TmpStamp.AddMinutes(-TZ_offset);
1112         }
1113       else if (datestr[19] == 'Z')
1114         {
1115           /* act as if the offset were +00:00 */
1116           char_count++;
1117         }
1118     }
1119
1120   if ( datestr[char_count] != 0 )
1121     {
1122       Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
1123                                    datestr, char_count);
1124       return false;
1125     }
1126
1127 #ifdef KM_WIN32
1128   SYSTEMTIME st;
1129   FILETIME ft;
1130   TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
1131   if ( SystemTimeToFileTime(&st, &ft) == 0 )
1132     return false;
1133   SYSTIME_TO_TIMESTAMP(&st, *this);
1134 #else
1135   Kumu::TAI::tai t;
1136   Kumu::TAI::caltime ct;
1137   TIMESTAMP_TO_CALTIME(TmpStamp, &ct);
1138   t = ct; // back and forth to tai to normalize offset
1139   ct = t;
1140   CALTIME_TO_TIMESTAMP(&ct, *this)
1141 #endif
1142
1143   return true;
1144 }
1145
1146 //
1147 bool
1148 Kumu::Timestamp::HasValue() const
1149 {
1150   if ( Year || Month || Day || Hour || Minute || Second )
1151     return true;
1152
1153   return false;
1154 }
1155
1156 //
1157 bool
1158 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
1159 {
1160   assert(Reader);
1161   if ( ! Reader->ReadUi16BE(&Year) ) return false;      
1162   if ( ! Reader->ReadRaw(&Month, 6) ) return false;
1163   return true;
1164 }
1165
1166 //
1167 bool
1168 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
1169 {
1170   assert(Writer);
1171   if ( ! Writer->WriteUi16BE(Year) ) return false;      
1172   if ( ! Writer->WriteRaw(&Month, 6) ) return false;
1173   return true;
1174 }
1175
1176 //
1177 long
1178 Kumu::Timestamp::GetSecondsSinceEpoch(void) const
1179 {
1180 #ifdef KM_WIN32
1181   SYSTEMTIME timeST;
1182   TIMESTAMP_TO_SYSTIME(*this, &timeST);
1183   FILETIME timeFT;
1184   SystemTimeToFileTime(&timeST, &timeFT);
1185   ULARGE_INTEGER timeUL;
1186   timeUL.LowPart = timeFT.dwLowDateTime;
1187   timeUL.HighPart = timeFT.dwHighDateTime;
1188
1189   SYSTEMTIME epochST;
1190   epochST.wYear = 1970;
1191   epochST.wMonth = 0;
1192   epochST.wDayOfWeek = 4;
1193   epochST.wDay = 1;
1194   epochST.wHour = 0;
1195   epochST.wMinute = 0;
1196   epochST.wSecond = 0;
1197   epochST.wMilliseconds = 0;
1198   FILETIME epochFT;
1199   SystemTimeToFileTime(&epochST, &epochFT);
1200   ULARGE_INTEGER epochUL;
1201   epochUL.LowPart = epochFT.dwLowDateTime;
1202   epochUL.HighPart = epochFT.dwHighDateTime;
1203
1204   return (timeUL.QuadPart - epochUL.QuadPart) / 10000000;
1205 #else
1206   Kumu::TAI::caltime ct;
1207   Kumu::TAI::tai t;
1208   TIMESTAMP_TO_CALTIME(*this, &ct);
1209   t = ct;
1210
1211   return (long) (t.x - ui64_C(4611686018427387914));
1212 #endif
1213 }
1214
1215 //------------------------------------------------------------------------------------------
1216
1217 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
1218   : m_p(0), m_capacity(0), m_size(0)
1219 {
1220   m_p = Buf->Data();
1221   m_capacity = Buf->Capacity();
1222   assert(m_p); assert(m_capacity);
1223 }
1224
1225 bool
1226 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
1227 {
1228   if ( ( m_size + ber_len ) > m_capacity )
1229     return false;
1230
1231   if ( ! write_BER(m_p + m_size, i, ber_len) )
1232     return false;
1233
1234   m_size += ber_len;
1235   return true;
1236 }
1237
1238
1239 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1240   : m_p(0), m_capacity(0), m_size(0)
1241 {
1242   m_p = Buf->RoData();
1243   m_capacity = Buf->Length();
1244   assert(m_p); assert(m_capacity);
1245 }
1246
1247 bool
1248 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1249 {
1250   if ( i == 0 || ber_len == 0 ) return false;
1251
1252   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1253     return false;
1254
1255   if ( ( m_size + *ber_len ) > m_capacity )
1256     return false;
1257
1258   if ( ! read_BER(m_p + m_size, i) )
1259     return false;
1260
1261   m_size += *ber_len;
1262   return true;
1263 }
1264
1265 //------------------------------------------------------------------------------------------
1266
1267 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1268
1269 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1270 {
1271   Capacity(cap);
1272 }
1273
1274 Kumu::ByteString::~ByteString()
1275 {
1276   if ( m_Data != 0 )
1277     free(m_Data);
1278 }
1279
1280
1281 // copy the given data into the ByteString, set Length value.
1282 // Returns error if the ByteString is too small.
1283 Kumu::Result_t
1284 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1285 {
1286   if ( m_Capacity < buf_len )
1287     return RESULT_ALLOC;
1288
1289   memcpy(m_Data, buf, buf_len);
1290   m_Length = buf_len;
1291   return RESULT_OK;
1292 }
1293
1294
1295 // copy the given data into the ByteString, set Length value.
1296 // Returns error if the ByteString is too small.
1297 Kumu::Result_t
1298 Kumu::ByteString::Set(const ByteString& Buf)
1299 {
1300   if ( m_Capacity < Buf.m_Capacity )
1301     return RESULT_ALLOC;
1302
1303   memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1304   m_Length = Buf.m_Length;
1305   return RESULT_OK;
1306 }
1307
1308
1309 // Sets the size of the internally allocate buffer.
1310 Kumu::Result_t
1311 Kumu::ByteString::Capacity(ui32_t cap_size)
1312 {
1313   if ( m_Capacity >= cap_size )
1314     return RESULT_OK;
1315
1316   byte_t* tmp_data = 0;
1317   if ( m_Data != 0 )
1318     {
1319       if ( m_Length > 0 )
1320         tmp_data = m_Data;
1321       else
1322         free(m_Data);
1323     }
1324                 
1325   if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1326     return RESULT_ALLOC;
1327
1328   if ( tmp_data != 0 )
1329     {
1330       assert(m_Length > 0);
1331       memcpy(m_Data, tmp_data, m_Length);
1332       free(tmp_data);
1333     }
1334                 
1335   m_Capacity = cap_size;
1336   return RESULT_OK;
1337 }
1338
1339 //
1340 Kumu::Result_t
1341 Kumu::ByteString::Append(const ByteString& Buf)
1342 {
1343   Result_t result = RESULT_OK;
1344   ui32_t diff = m_Capacity - m_Length;
1345
1346   if ( diff < Buf.Length() )
1347     result = Capacity(m_Capacity + Buf.Length());
1348
1349   if ( KM_SUCCESS(result) )
1350     {
1351       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1352       m_Length += Buf.Length();
1353     }
1354
1355   return result;
1356 }
1357
1358 //
1359 Kumu::Result_t
1360 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1361 {
1362   Result_t result = RESULT_OK;
1363   ui32_t diff = m_Capacity - m_Length;
1364
1365   if ( diff < buf_len )
1366     result = Capacity(m_Capacity + buf_len);
1367
1368   if ( KM_SUCCESS(result) )
1369     {
1370       memcpy(m_Data + m_Length, buf, buf_len);
1371       m_Length += buf_len;
1372     }
1373
1374   return result;
1375 }
1376
1377
1378 //
1379 // end KM_util.cpp
1380 //