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