Update license.
[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 const char*
43 Kumu::Version()
44 {
45   return PACKAGE_VERSION;
46 }
47
48
49 //------------------------------------------------------------------------------------------
50
51 // Result_t Internals
52
53 struct map_entry_t
54 {
55   int             rcode;
56   Kumu::Result_t* result;
57 };
58
59 const ui32_t MapMax = 1024;
60 const ui32_t MapSize = MapMax * (sizeof(struct map_entry_t));
61 static bool s_MapInit = false;
62 static struct map_entry_t s_ResultMap[MapSize];
63
64 //
65 const Kumu::Result_t&
66 Kumu::Result_t::Find(int v)
67 {
68   if ( v == 0 )
69     return RESULT_OK;
70
71   for ( ui32_t i = 0; s_ResultMap[i].result != 0 && i < MapMax; i++ )
72     {
73       if ( s_ResultMap[i].rcode == v )
74         return *s_ResultMap[i].result;
75     }
76
77   DefaultLogSink().Error("Unknown result code: %ld\n", v);
78   return RESULT_FAIL;
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
376 // convert a memory region to a NULL-terminated hexadecimal string
377 //
378 const char*
379 Kumu::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
380 {
381   if ( bin_buf == 0
382        || str_buf == 0
383        || ((bin_len * 2) + 1) > str_len )
384     return 0;
385
386   char* p = str_buf;
387
388   for ( ui32_t i = 0; i < bin_len; i++ )
389     {
390       *p = (bin_buf[i] >> 4) & 0x0f;
391       *p += *p < 10 ? 0x30 : 0x61 - 10;
392       p++;
393
394       *p = bin_buf[i] & 0x0f;
395       *p += *p < 10 ? 0x30 : 0x61 - 10;
396       p++;
397     }
398
399   *p = '\0';
400   return str_buf;
401 }
402
403
404 // spew a range of bin data as hex
405 void
406 Kumu::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream)
407 {
408   if ( buf == 0 )
409     return;
410
411   if ( stream == 0 )
412     stream = stderr;
413
414   static ui32_t row_len = 16;
415   const byte_t* p = buf;
416   const byte_t* end_p = p + dump_len;
417
418   for ( ui32_t line = 0; p < end_p; line++ )
419     {
420       fprintf(stream, "  %06x: ", line);
421       ui32_t i;
422       const byte_t* pp;
423
424       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
425         fprintf(stream, "%02x ", *pp);
426
427       while ( i++ < row_len )
428         fputs("   ", stream);
429
430       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
431         fputc((isprint(*pp) ? *pp : '.'), stream);
432
433       fputc('\n', stream);
434       p += row_len;
435     }
436 }
437
438 //
439 const char*
440 Kumu::bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
441 {
442   ui32_t i, j, k;
443
444   if ( str_len < 34 || bin_len != UUID_Length )
445     return 0;
446
447   if ( bin2hex(bin_buf, bin_len, str_buf, str_len) == 0 )
448     return 0;
449
450   // shift the node id
451   for ( k = 19, i = 12; i > 0; i-- )
452     str_buf[k+i+4] = str_buf[k+i];
453
454   // shift the time (mid+hi+clk)
455   for ( k = 15, j = 3; k > 6; k -= 4, j-- )
456     {
457       for ( i = 4; i > 0; i-- )
458         str_buf[k+i+j] = str_buf[k+i];
459     }
460
461   // add in the hyphens and trainling null
462   for ( i = 8; i < 24; i += 5 )
463     str_buf[i] = '-';
464   
465   str_buf[36] = 0;
466   return str_buf;
467 }
468
469 //
470 void
471 Kumu::GenRandomValue(UUID& ID)
472 {
473   byte_t tmp_buf[UUID_Length];
474   GenRandomUUID(tmp_buf);
475   ID.Set(tmp_buf);
476 }
477
478 //
479 void
480 Kumu::GenRandomUUID(byte_t* buf)
481 {
482   FortunaRNG RNG;
483   RNG.FillRandom(buf, UUID_Length);
484   buf[6] &= 0x0f; // clear bits 4-7
485   buf[6] |= 0x40; // set UUID version
486   buf[8] &= 0x3f; // clear bits 6&7
487   buf[8] |= 0x80; // set bit 7
488 }
489
490 //
491 void
492 Kumu::GenRandomValue(SymmetricKey& Key)
493 {
494   byte_t tmp_buf[SymmetricKey_Length];
495   FortunaRNG RNG;
496   RNG.FillRandom(tmp_buf, SymmetricKey_Length);
497   Key.Set(tmp_buf);
498 }
499
500
501 //------------------------------------------------------------------------------------------
502 // read a ber value from the buffer and compare with test value.
503 // Advances buffer to first character after BER value.
504
505 // read a ber value from the buffer and compare with test value.
506 // Advances buffer to first character after BER value.
507 //
508 bool
509 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
510 {
511   if ( buf == 0 )
512     return false;
513
514   if ( ( **buf & 0x80 ) == 0 )
515     return false;
516
517   ui64_t val = 0;
518   ui8_t ber_size = ( **buf & 0x0f ) + 1;
519
520   if ( ber_size > 9 )
521     return false;
522
523   for ( ui8_t i = 1; i < ber_size; i++ )
524     {
525       if ( (*buf)[i] > 0 )
526         val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
527     }
528
529   *buf += ber_size;
530   return ( val == test_value );
531 }
532
533
534 //
535 bool
536 Kumu::read_BER(const byte_t* buf, ui64_t* val)
537 {
538   ui8_t ber_size, i;
539   
540   if ( buf == 0 || val == 0 )
541     return false;
542
543   if ( ( *buf & 0x80 ) == 0 )
544     return false;
545
546   *val = 0;
547   ber_size = ( *buf & 0x0f ) + 1;
548
549   if ( ber_size > 9 )
550     return false;
551
552   for ( i = 1; i < ber_size; i++ )
553     {
554       if ( buf[i] > 0 )
555         *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
556     }
557
558   return true;
559 }
560
561
562 static const ui64_t ber_masks[9] =
563   { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), 
564     ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
565     ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
566     ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
567     0
568   };
569
570
571 //
572 bool
573 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
574 {
575   if ( buf == 0 )
576     return false;
577
578   if ( ber_len == 0 )
579     { // calculate default length
580       if ( val < 0x01000000L )
581         ber_len = 4;
582       else if ( val < ui64_C(0x0100000000000000) )
583         ber_len = 8;
584       else
585         ber_len = 9;
586     }
587   else
588     { // sanity check BER length
589       if ( ber_len > 9 )
590         {
591           DefaultLogSink().Error("BER size %u exceeds maximum size of 9\n", ber_len);
592           return false;
593         }
594       
595       if ( val & ber_masks[ber_len - 1] )
596         {
597           ui64Printer tmp_i(val);
598           DefaultLogSink().Error("BER size %u too small for value %s\n", tmp_i.c_str());
599           return false;
600         }
601     }
602
603   buf[0] = 0x80 + ( ber_len - 1 );
604
605   for ( ui32_t i = ber_len - 1; i > 0; i-- )
606     {
607       buf[i] = (ui8_t)(val & 0xff);
608       val >>= 8;
609     }
610
611   return true;
612 }
613
614
615 //------------------------------------------------------------------------------------------
616 #ifdef KM_WIN32
617
618 #define TIMESTAMP_TO_SYSTIME(ts, t) \
619   (t)->wYear    = (ts).Year;   /* year */ \
620   (t)->wMonth   = (ts).Month;  /* month of year (1 - 12) */ \
621   (t)->wDay     = (ts).Day;    /* day of month (1 - 31) */ \
622   (t)->wHour    = (ts).Hour;   /* hours (0 - 23) */ \
623   (t)->wMinute  = (ts).Minute; /* minutes (0 - 59) */ \
624   (t)->wSecond  = (ts).Second; /* seconds (0 - 60) */ \
625   (t)->wDayOfWeek = 0; \
626   (t)->wMilliseconds = 0
627
628 #define SYSTIME_TO_TIMESTAMP(t, ts) \
629   (ts).Year   = (t)->wYear;    /* year */ \
630   (ts).Month  = (t)->wMonth;   /* month of year (1 - 12) */ \
631   (ts).Day    = (t)->wDay;     /* day of month (1 - 31) */ \
632   (ts).Hour   = (t)->wHour;    /* hours (0 - 23) */ \
633   (ts).Minute = (t)->wMinute;  /* minutes (0 - 59) */ \
634   (ts).Second = (t)->wSecond;  /* seconds (0 - 60) */
635
636 //
637 Kumu::Timestamp::Timestamp() :
638   Year(0), Month(0),  Day(0), Hour(0), Minute(0), Second(0)
639 {
640   SYSTEMTIME sys_time;
641   GetSystemTime(&sys_time);
642   SYSTIME_TO_TIMESTAMP(&sys_time, *this);
643 }
644
645 //
646 bool
647 Kumu::Timestamp::operator<(const Timestamp& rhs) const
648 {
649   SYSTEMTIME lhst, rhst;
650   FILETIME lft, rft;
651
652   TIMESTAMP_TO_SYSTIME(*this, &lhst);
653   TIMESTAMP_TO_SYSTIME(rhs, &rhst);
654   SystemTimeToFileTime(&lhst, &lft);
655   SystemTimeToFileTime(&rhst, &rft);
656   return ( CompareFileTime(&lft, &rft) == -1 );
657 }
658
659 //
660 bool
661 Kumu::Timestamp::operator>(const Timestamp& rhs) const
662 {
663   SYSTEMTIME lhst, rhst;
664   FILETIME lft, rft;
665
666   TIMESTAMP_TO_SYSTIME(*this, &lhst);
667   TIMESTAMP_TO_SYSTIME(rhs, &rhst);
668   SystemTimeToFileTime(&lhst, &lft);
669   SystemTimeToFileTime(&rhst, &rft);
670   return ( CompareFileTime(&lft, &rft) == 1 );
671 }
672
673 inline ui64_t
674 seconds_to_ns100(ui32_t seconds)
675 {
676   return ((ui64_t)seconds * 10000000);
677 }
678
679 //
680 void
681 Kumu::Timestamp::AddDays(i32_t days)
682 {
683   SYSTEMTIME current_st;
684   FILETIME current_ft;
685   ULARGE_INTEGER current_ul;
686
687   if ( days != 0 )
688     {
689       TIMESTAMP_TO_SYSTIME(*this, &current_st);
690       SystemTimeToFileTime(&current_st, &current_ft);
691       memcpy(&current_ul, &current_ft, sizeof(current_ul));
692       current_ul.QuadPart += ( seconds_to_ns100(86400) * (ui64_t)days );
693       memcpy(&current_ft, &current_ul, sizeof(current_ft));
694       FileTimeToSystemTime(&current_ft, &current_st);
695       SYSTIME_TO_TIMESTAMP(&current_st, *this);
696     }
697 }
698
699 //
700 void
701 Kumu::Timestamp::AddHours(i32_t hours)
702 {
703   SYSTEMTIME current_st;
704   FILETIME current_ft;
705   ULARGE_INTEGER current_ul;
706
707   if ( hours != 0 )
708     {
709       TIMESTAMP_TO_SYSTIME(*this, &current_st);
710       SystemTimeToFileTime(&current_st, &current_ft);
711       memcpy(&current_ul, &current_ft, sizeof(current_ul));
712       current_ul.QuadPart += ( seconds_to_ns100(3600) * (ui64_t)hours );
713       memcpy(&current_ft, &current_ul, sizeof(current_ft));
714       FileTimeToSystemTime(&current_ft, &current_st);
715       SYSTIME_TO_TIMESTAMP(&current_st, *this);
716     }
717 }
718
719 #else // KM_WIN32
720
721 #include <time.h>
722
723 #define TIMESTAMP_TO_TM(ts, t) \
724   (t)->tm_year = (ts).Year - 1900;   /* year - 1900 */ \
725   (t)->tm_mon  = (ts).Month - 1;     /* month of year (0 - 11) */ \
726   (t)->tm_mday = (ts).Day;           /* day of month (1 - 31) */ \
727   (t)->tm_hour = (ts).Hour;          /* hours (0 - 23) */ \
728   (t)->tm_min  = (ts).Minute;        /* minutes (0 - 59) */ \
729   (t)->tm_sec  = (ts).Second;        /* seconds (0 - 60) */
730
731 #define TM_TO_TIMESTAMP(t, ts) \
732   (ts).Year   = (t)->tm_year + 1900;    /* year - 1900 */ \
733   (ts).Month  = (t)->tm_mon + 1;     /* month of year (0 - 11) */ \
734   (ts).Day    = (t)->tm_mday;    /* day of month (1 - 31) */ \
735   (ts).Hour   = (t)->tm_hour;    /* hours (0 - 23) */ \
736   (ts).Minute = (t)->tm_min;     /* minutes (0 - 59) */ \
737   (ts).Second = (t)->tm_sec;     /* seconds (0 - 60) */
738
739 //
740 Kumu::Timestamp::Timestamp() :
741   Year(0), Month(0),  Day(0), Hour(0), Minute(0), Second(0)
742 {
743   time_t t_now = time(0);
744   struct tm*  now = gmtime(&t_now);
745   TM_TO_TIMESTAMP(now, *this);
746 }
747
748 //
749 bool
750 Kumu::Timestamp::operator<(const Timestamp& rhs) const
751 {
752   struct tm lhtm, rhtm;
753   TIMESTAMP_TO_TM(*this, &lhtm);
754   TIMESTAMP_TO_TM(rhs, &rhtm);
755   return ( timegm(&lhtm) < timegm(&rhtm) );
756 }
757
758 //
759 bool
760 Kumu::Timestamp::operator>(const Timestamp& rhs) const
761 {
762   struct tm lhtm, rhtm;
763   TIMESTAMP_TO_TM(*this, &lhtm);
764   TIMESTAMP_TO_TM(rhs, &rhtm);
765   return ( timegm(&lhtm) > timegm(&rhtm) );
766 }
767
768 //
769 void
770 Kumu::Timestamp::AddDays(i32_t days)
771 {
772   struct tm current;
773
774   if ( days != 0 )
775     {
776       TIMESTAMP_TO_TM(*this, &current);
777       time_t adj_time = timegm(&current);
778       adj_time += 86400 * days;
779       struct tm*  now = gmtime(&adj_time);
780       TM_TO_TIMESTAMP(now, *this);
781     }
782 }
783
784 //
785 void
786 Kumu::Timestamp::AddHours(i32_t hours)
787 {
788   struct tm current;
789
790   if ( hours != 0 )
791     {
792       TIMESTAMP_TO_TM(*this, &current);
793       time_t adj_time = timegm(&current);
794       adj_time += 3600 * hours;
795       struct tm*  now = gmtime(&adj_time);
796       TM_TO_TIMESTAMP(now, *this);
797     }
798 }
799
800 #endif // KM_WIN32
801
802
803 Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
804 {
805   Year   = rhs.Year;
806   Month  = rhs.Month;
807   Day    = rhs.Day;
808   Hour   = rhs.Hour;
809   Minute = rhs.Minute;
810   Second = rhs.Second;
811 }
812
813 Kumu::Timestamp::~Timestamp()
814 {
815 }
816
817 //
818 const Kumu::Timestamp&
819 Kumu::Timestamp::operator=(const Timestamp& rhs)
820 {
821   Year   = rhs.Year;
822   Month  = rhs.Month;
823   Day    = rhs.Day;
824   Hour   = rhs.Hour;
825   Minute = rhs.Minute;
826   Second = rhs.Second;
827   return *this;
828 }
829
830 //
831 bool
832 Kumu::Timestamp::operator==(const Timestamp& rhs) const
833 {
834   if ( Year == rhs.Year
835        && Month  == rhs.Month
836        && Day    == rhs.Day
837        && Hour   == rhs.Hour
838        && Minute == rhs.Minute
839        && Second == rhs.Second )
840     return true;
841
842   return false;
843 }
844
845 //
846 bool
847 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
848 {
849   if ( Year != rhs.Year
850        || Month  != rhs.Month
851        || Day    != rhs.Day
852        || Hour   != rhs.Hour
853        || Minute != rhs.Minute
854        || Second != rhs.Second )
855     return true;
856
857   return false;
858 }
859
860 // 
861 const char*
862 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
863 {
864   if ( buf_len < ( DateTimeLen + 1 ) )
865     return 0;
866
867   // 2004-05-01T13:20:00-00:00
868   snprintf(str_buf, buf_len,
869            "%04hu-%02hu-%02huT%02hu:%02hu:%02hu+00:00",
870            Year, Month, Day, Hour, Minute, Second);
871   
872   return str_buf;
873 }
874
875 //
876 bool
877 Kumu::Timestamp::DecodeString(const char* datestr)
878 {
879   Timestamp TmpStamp;
880
881   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
882        || datestr[4] != '-'
883        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
884        || datestr[7] != '-'
885        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
886     return false;
887
888   ui32_t char_count = 10;
889   TmpStamp.Year = atoi(datestr);
890   TmpStamp.Month = atoi(datestr + 5);
891   TmpStamp.Day = atoi(datestr + 8);
892   TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
893  
894   if ( datestr[10] == 'T' )
895     {
896       if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
897            || datestr[13] != ':'
898            || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
899         return false;
900
901       char_count += 6;
902       TmpStamp.Hour = atoi(datestr + 11);
903       TmpStamp.Minute = atoi(datestr + 14);
904
905       if ( datestr[16] == ':' )
906         {
907           if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
908             return false;
909
910           char_count += 3;
911           TmpStamp.Second = atoi(datestr + 17);
912         }
913
914       if ( datestr[19] == '.' )
915         {
916           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
917             return false;
918           
919           // we don't carry the ms value
920           datestr += 4;
921         }
922
923       if ( datestr[19] == '-' || datestr[19] == '+' )
924         {
925           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
926                || datestr[22] != ':'
927                || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
928             return false;
929
930           char_count += 6;
931           ui32_t TZ_hh = atoi(datestr + 20);
932           ui32_t TZ_mm = atoi(datestr + 23);
933       
934           if ( TZ_mm != 0 )
935             DefaultLogSink().Warn("Ignoring minutes in timezone offset: %u\n", TZ_mm);
936           
937           if ( TZ_hh > 12 )
938             return false;
939
940           else 
941             AddHours( (datestr[19] == '-' ? (0 - TZ_hh) : TZ_hh));
942         }
943     }
944
945   if ( datestr[char_count] != 0 )
946     {
947       DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
948                              datestr, char_count);
949       return false;
950     }
951
952 #ifdef KM_WIN32
953   SYSTEMTIME st;
954   FILETIME ft;
955   TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
956   if ( SystemTimeToFileTime(&st, &ft) == 0 )
957     return false;
958   SYSTIME_TO_TIMESTAMP(&st, *this);
959 #else
960   struct tm stm;
961   TIMESTAMP_TO_TM(TmpStamp, &stm);
962   if ( timegm(&stm) == 0 )
963     return false;
964   TM_TO_TIMESTAMP(&stm, *this);
965 #endif
966
967   return true;
968 }
969
970 //
971 bool
972 Kumu::Timestamp::HasValue() const
973 {
974   if ( Year || Month || Day || Hour || Minute || Second )
975     return true;
976
977   return false;
978 }
979
980 //
981 bool
982 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
983 {
984   assert(Reader);
985   if ( ! Reader->ReadUi16BE(&Year) ) return false;      
986   if ( ! Reader->ReadRaw(&Month, 6) ) return false;
987   return true;
988 }
989
990 //
991 bool
992 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
993 {
994   assert(Writer);
995   if ( ! Writer->WriteUi16BE(Year) ) return false;      
996   if ( ! Writer->WriteRaw(&Month, 6) ) return false;
997   return true;
998 }
999
1000 //------------------------------------------------------------------------------------------
1001
1002 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
1003   : m_p(0), m_capacity(0), m_size(0)
1004 {
1005   m_p = Buf->Data();
1006   m_capacity = Buf->Capacity();
1007   assert(m_p); assert(m_capacity);
1008 }
1009
1010 bool
1011 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
1012 {
1013   if ( ( m_size + ber_len ) > m_capacity )
1014     return false;
1015
1016   if ( ! write_BER(m_p + m_size, i, ber_len) )
1017     return false;
1018
1019   m_size += ber_len;
1020   return true;
1021 }
1022
1023
1024 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1025   : m_p(0), m_capacity(0), m_size(0)
1026 {
1027   m_p = Buf->RoData();
1028   m_capacity = Buf->Length();
1029   assert(m_p); assert(m_capacity);
1030 }
1031
1032 bool
1033 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1034 {
1035   if ( i == 0 || ber_len == 0 ) return false;
1036
1037   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1038     return false;
1039
1040   if ( ( m_size + *ber_len ) > m_capacity )
1041     return false;
1042
1043   if ( ! read_BER(m_p + m_size, i) )
1044     return false;
1045
1046   m_size += *ber_len;
1047   return true;
1048 }
1049
1050 //------------------------------------------------------------------------------------------
1051
1052 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1053
1054 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1055 {
1056   Capacity(cap);
1057 }
1058
1059 Kumu::ByteString::~ByteString()
1060 {
1061   if ( m_Data != 0 )
1062     free(m_Data);
1063 }
1064
1065
1066 // copy the given data into the ByteString, set Length value.
1067 // Returns error if the ByteString is too small.
1068 Kumu::Result_t
1069 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1070 {
1071   if ( m_Capacity < buf_len )
1072     return RESULT_ALLOC;
1073
1074   memcpy(m_Data, buf, buf_len);
1075   m_Length = buf_len;
1076   return RESULT_OK;
1077 }
1078
1079
1080 // copy the given data into the ByteString, set Length value.
1081 // Returns error if the ByteString is too small.
1082 Kumu::Result_t
1083 Kumu::ByteString::Set(const ByteString& Buf)
1084 {
1085   if ( m_Capacity < Buf.m_Capacity )
1086     return RESULT_ALLOC;
1087
1088   memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1089   m_Length = Buf.m_Length;
1090   return RESULT_OK;
1091 }
1092
1093
1094 // Sets the size of the internally allocate buffer.
1095 Kumu::Result_t
1096 Kumu::ByteString::Capacity(ui32_t cap_size)
1097 {
1098   if ( m_Capacity >= cap_size )
1099     return RESULT_OK;
1100
1101   byte_t* tmp_data = 0;
1102   if ( m_Data != 0 )
1103     {
1104       if ( m_Length > 0 )
1105         tmp_data = m_Data;
1106       else
1107         free(m_Data);
1108     }
1109                 
1110   if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1111     return RESULT_ALLOC;
1112
1113   if ( tmp_data != 0 )
1114     {
1115       assert(m_Length > 0);
1116       memcpy(m_Data, tmp_data, m_Length);
1117       free(tmp_data);
1118     }
1119                 
1120   m_Capacity = cap_size;
1121   return RESULT_OK;
1122 }
1123
1124 //
1125 Kumu::Result_t
1126 Kumu::ByteString::Append(const ByteString& Buf)
1127 {
1128   Result_t result = RESULT_OK;
1129   ui32_t diff = m_Capacity - m_Length;
1130
1131   if ( diff < Buf.Length() )
1132     result = Capacity(m_Capacity + Buf.Length());
1133
1134   if ( KM_SUCCESS(result) )
1135     {
1136       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1137       m_Length += Buf.Length();
1138     }
1139
1140   return result;
1141 }
1142
1143 //
1144 Kumu::Result_t
1145 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1146 {
1147   Result_t result = RESULT_OK;
1148   ui32_t diff = m_Capacity - m_Length;
1149
1150   if ( diff < buf_len )
1151     result = Capacity(m_Capacity + buf_len);
1152
1153   if ( KM_SUCCESS(result) )
1154     {
1155       memcpy(m_Data + m_Length, buf, buf_len);
1156       m_Length += buf_len;
1157     }
1158
1159   return result;
1160 }
1161
1162
1163 //
1164 // end KM_util.cpp
1165 //