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