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