2006/06/23 tmccolm
[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(str);
271   KM_TEST_NULL(buf);
272   KM_TEST_NULL(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 inline ui64_t
604 seconds_to_ns100(ui32_t seconds)
605 {
606   return ((ui64_t)seconds * 10000000);
607 }
608
609 //
610 void
611 Kumu::Timestamp::AddDays(i32_t days)
612 {
613   SYSTEMTIME current_st;
614   FILETIME current_ft;
615   ULARGE_INTEGER current_ul;
616
617   if ( days != 0 )
618     {
619       TIMESTAMP_TO_SYSTIME(*this, &current_st);
620       SystemTimeToFileTime(&current_st, &current_ft);
621       memcpy(&current_ul, &current_ft, sizeof(current_ul));
622       current_ul.QuadPart += ( seconds_to_ns100(86400) * (ui64_t)days );
623       memcpy(&current_ft, &current_ul, sizeof(current_ft));
624       FileTimeToSystemTime(&current_ft, &current_st);
625       SYSTIME_TO_TIMESTAMP(&current_st, *this);
626     }
627 }
628
629 //
630 void
631 Kumu::Timestamp::AddHours(i32_t hours)
632 {
633   SYSTEMTIME current_st;
634   FILETIME current_ft;
635   ULARGE_INTEGER current_ul;
636
637   if ( hours != 0 )
638     {
639       TIMESTAMP_TO_SYSTIME(*this, &current_st);
640       SystemTimeToFileTime(&current_st, &current_ft);
641       memcpy(&current_ul, &current_ft, sizeof(current_ul));
642       current_ul.QuadPart += ( seconds_to_ns100(3600) * (ui64_t)hours );
643       memcpy(&current_ft, &current_ul, sizeof(current_ft));
644       FileTimeToSystemTime(&current_ft, &current_st);
645       SYSTIME_TO_TIMESTAMP(&current_st, *this);
646     }
647 }
648
649 #else // KM_WIN32
650
651 #include <time.h>
652
653 #define TIMESTAMP_TO_TM(ts, t) \
654   (t)->tm_year = (ts).Year - 1900;   /* year - 1900 */ \
655   (t)->tm_mon  = (ts).Month - 1;     /* month of year (0 - 11) */ \
656   (t)->tm_mday = (ts).Day;           /* day of month (1 - 31) */ \
657   (t)->tm_hour = (ts).Hour;          /* hours (0 - 23) */ \
658   (t)->tm_min  = (ts).Minute;        /* minutes (0 - 59) */ \
659   (t)->tm_sec  = (ts).Second;        /* seconds (0 - 60) */
660
661 #define TM_TO_TIMESTAMP(t, ts) \
662   (ts).Year   = (t)->tm_year + 1900;    /* year - 1900 */ \
663   (ts).Month  = (t)->tm_mon + 1;     /* month of year (0 - 11) */ \
664   (ts).Day    = (t)->tm_mday;    /* day of month (1 - 31) */ \
665   (ts).Hour   = (t)->tm_hour;    /* hours (0 - 23) */ \
666   (ts).Minute = (t)->tm_min;     /* minutes (0 - 59) */ \
667   (ts).Second = (t)->tm_sec;     /* seconds (0 - 60) */
668
669 //
670 Kumu::Timestamp::Timestamp() :
671   Year(0), Month(0),  Day(0), Hour(0), Minute(0), Second(0)
672 {
673   time_t t_now = time(0);
674   struct tm*  now = gmtime(&t_now);
675   TM_TO_TIMESTAMP(now, *this);
676 }
677
678 //
679 bool
680 Kumu::Timestamp::operator<(const Timestamp& rhs) const
681 {
682   struct tm lhtm, rhtm;
683   TIMESTAMP_TO_TM(*this, &lhtm);
684   TIMESTAMP_TO_TM(rhs, &rhtm);
685   return ( timegm(&lhtm) < timegm(&rhtm) );
686 }
687
688 //
689 void
690 Kumu::Timestamp::AddDays(i32_t days)
691 {
692   struct tm current;
693
694   if ( days != 0 )
695     {
696       TIMESTAMP_TO_TM(*this, &current);
697       time_t adj_time = timegm(&current);
698       adj_time += 86400 * days;
699       struct tm*  now = gmtime(&adj_time);
700       TM_TO_TIMESTAMP(now, *this);
701     }
702 }
703
704 //
705 void
706 Kumu::Timestamp::AddHours(i32_t hours)
707 {
708   struct tm current;
709
710   if ( hours != 0 )
711     {
712       TIMESTAMP_TO_TM(*this, &current);
713       time_t adj_time = timegm(&current);
714       adj_time += 3600 * hours;
715       struct tm*  now = gmtime(&adj_time);
716       TM_TO_TIMESTAMP(now, *this);
717     }
718 }
719
720 #endif // KM_WIN32
721
722
723 Kumu::Timestamp::Timestamp(const Timestamp& rhs)
724 {
725   Year   = rhs.Year;
726   Month  = rhs.Month;
727   Day    = rhs.Day;
728   Hour   = rhs.Hour;
729   Minute = rhs.Minute;
730   Second = rhs.Second;
731 }
732
733 Kumu::Timestamp::~Timestamp()
734 {
735 }
736
737 //
738 const Kumu::Timestamp&
739 Kumu::Timestamp::operator=(const Timestamp& rhs)
740 {
741   Year   = rhs.Year;
742   Month  = rhs.Month;
743   Day    = rhs.Day;
744   Hour   = rhs.Hour;
745   Minute = rhs.Minute;
746   Second = rhs.Second;
747   return *this;
748 }
749
750 //
751 bool
752 Kumu::Timestamp::operator==(const Timestamp& rhs) const
753 {
754   if ( Year == rhs.Year
755        && Month  == rhs.Month
756        && Day    == rhs.Day
757        && Hour   == rhs.Hour
758        && Minute == rhs.Minute
759        && Second == rhs.Second )
760     return true;
761
762   return false;
763 }
764
765 //
766 bool
767 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
768 {
769   if ( Year != rhs.Year
770        || Month  != rhs.Month
771        || Day    != rhs.Day
772        || Hour   != rhs.Hour
773        || Minute != rhs.Minute
774        || Second != rhs.Second )
775     return true;
776
777   return false;
778 }
779
780 // 
781 const char*
782 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
783 {
784   if ( buf_len < ( DateTimeLen + 1 ) )
785     return 0;
786
787   // 2004-05-01T13:20:00-00:00
788   snprintf(str_buf, buf_len,
789            "%04hu-%02hu-%02huT%02hu:%02hu:%02hu-00:00",
790            Year, Month, Day, Hour, Minute, Second);
791   
792   return str_buf;
793 }
794
795 //
796 bool
797 Kumu::Timestamp::DecodeString(const char* datestr)
798 {
799   Timestamp TmpStamp;
800
801   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
802        || datestr[4] != '-'
803        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
804        || datestr[7] != '-'
805        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
806     return false;
807
808   TmpStamp.Year = atoi(datestr);
809   TmpStamp.Month = atoi(datestr + 5);
810   TmpStamp.Day = atoi(datestr + 8);
811   TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
812  
813   if ( datestr[10] == 'T' )
814     {
815       if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
816            || datestr[13] != ':'
817            || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
818         return false;
819
820       TmpStamp.Hour = atoi(datestr + 11);
821       TmpStamp.Minute = atoi(datestr + 14);
822
823       if ( datestr[16] == ':' )
824         {
825           if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
826             return false;
827
828           TmpStamp.Second = atoi(datestr + 17);
829         }
830     }
831
832   if ( datestr[19] == '-' || datestr[19] == '+' )
833     {
834       if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
835            || datestr[22] != ':'
836            || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
837         return false;
838
839       ui32_t TZ_hh = atoi(datestr + 20);
840       ui32_t TZ_mm = atoi(datestr + 23);
841       
842       if ( TZ_mm != 0 )
843         DefaultLogSink().Error("Ignoring minutes in timezone offset: %u\n", TZ_mm);
844
845       if ( TZ_hh > 12 )
846         return false;
847
848       else 
849         AddHours( (datestr[19] == '-' ? (-TZ_hh) : TZ_hh));
850     }
851
852 #ifdef KM_WIN32
853   SYSTEMTIME st;
854   FILETIME ft;
855   TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
856   if ( SystemTimeToFileTime(&st, &ft) == 0 )
857     return false;
858   SYSTIME_TO_TIMESTAMP(&st, *this);
859 #else
860   struct tm stm;
861   TIMESTAMP_TO_TM(TmpStamp, &stm);
862   if ( timegm(&stm) == 0 )
863     return false;
864   TM_TO_TIMESTAMP(&stm, *this);
865 #endif
866
867   return true;
868 }
869
870 //
871 bool
872 Kumu::Timestamp::HasValue() const
873 {
874   if ( Year || Month || Day || Hour || Minute || Second )
875     return true;
876
877   return false;
878 }
879
880 //
881 bool
882 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
883 {
884   assert(Reader);
885   if ( ! Reader->ReadUi16BE(&Year) ) return false;      
886   if ( ! Reader->ReadRaw(&Month, 6) ) return false;
887   return true;
888 }
889
890 //
891 bool
892 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
893 {
894   assert(Writer);
895   if ( ! Writer->WriteUi16BE(Year) ) return false;      
896   if ( ! Writer->WriteRaw(&Month, 6) ) return false;
897   return true;
898 }
899
900 #if 0
901 //
902 bool
903 Kumu::UnarchiveString(MemIOReader* Reader, std::string&)
904 {
905   assert(Reader);
906   ui32_t str_length;
907   if ( ! Reader->ReadUi32BE(&str_length) ) return false;
908   assign((const char*)Reader->CurrentData(), str_length);
909   if ( ! Reader->SkipOffset(str_length) ) return false;
910   return true;
911 }
912
913 //
914 bool
915 Kumu::String::Archive(MemIOWriter* Writer) const
916 {
917   assert(Writer);
918   if ( ! Writer->WriteUi32BE(length()) ) return false;
919   if ( ! Writer->WriteRaw((const byte_t*)c_str(), length()) ) return false;
920
921   return true;
922 }
923 #endif
924
925 //------------------------------------------------------------------------------------------
926
927 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
928   : m_p(0), m_capacity(0), m_size(0)
929 {
930   m_p = Buf->Data();
931   m_capacity = Buf->Capacity();
932   assert(m_p); assert(m_capacity);
933 }
934
935 bool
936 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
937 {
938   if ( ( m_size + ber_len ) > m_capacity )
939     return false;
940
941   if ( ! write_BER(m_p + m_size, i, ber_len) )
942     return false;
943
944   m_size += ber_len;
945   return true;
946 }
947
948
949 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
950   : m_p(0), m_capacity(0), m_size(0)
951 {
952   m_p = Buf->RoData();
953   m_capacity = Buf->Capacity();
954   assert(m_p); assert(m_capacity);
955 }
956
957 bool
958 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
959 {
960   if ( i == 0 || ber_len == 0 ) return false;
961
962   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
963     return false;
964
965   if ( ( m_size + *ber_len ) > m_capacity )
966     return false;
967
968   if ( ! read_BER(m_p + m_size, i) )
969     return false;
970
971   m_size += *ber_len;
972   return true;
973 }
974
975 //------------------------------------------------------------------------------------------
976
977 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
978
979 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
980 {
981   Capacity(cap);
982 }
983
984 Kumu::ByteString::~ByteString()
985 {
986   if ( m_Data != 0 )
987     free(m_Data);
988 }
989
990
991 // copy the given data into the ByteString, set Length value.
992 // Returns error if the ByteString is too small.
993 Kumu::Result_t
994 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
995 {
996   if ( m_Capacity < buf_len )
997     return RESULT_ALLOC;
998
999   memcpy(m_Data, buf, buf_len);
1000   m_Length = buf_len;
1001   return RESULT_OK;
1002 }
1003
1004
1005 // Sets the size of the internally allocate buffer.
1006 // Resets content length to zero.
1007 Kumu::Result_t
1008 Kumu::ByteString::Capacity(ui32_t cap_size)
1009 {
1010   if ( m_Capacity < cap_size )
1011     {
1012       if ( m_Data != 0 )
1013         free(m_Data);
1014                 
1015       m_Data = (byte_t*)malloc(cap_size);
1016                 
1017       if ( m_Data == 0 )
1018         return RESULT_ALLOC;
1019                 
1020       m_Capacity = cap_size;
1021       m_Length = 0;
1022     }
1023         
1024   return RESULT_OK;
1025 }
1026
1027 //
1028 Kumu::Result_t
1029 Kumu::ByteString::Append(const ByteString& Buf)
1030 {
1031   Result_t result = RESULT_OK;
1032   ui32_t diff = m_Capacity - m_Length;
1033
1034   if ( diff < Buf.Length() )
1035     result = Capacity(m_Capacity + Buf.Length());
1036
1037   if ( KM_SUCCESS(result) )
1038     {
1039       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1040       m_Length += Buf.Length();
1041     }
1042
1043   return result;
1044 }
1045
1046 //
1047 Kumu::Result_t
1048 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1049 {
1050   Result_t result = RESULT_OK;
1051   ui32_t diff = m_Capacity - m_Length;
1052
1053   if ( diff < buf_len )
1054     result = Capacity(m_Capacity + buf_len);
1055
1056   if ( KM_SUCCESS(result) )
1057     {
1058       memcpy(m_Data + m_Length, buf, buf_len);
1059       m_Length += buf_len;
1060     }
1061
1062   return result;
1063 }
1064
1065
1066 //
1067 // end KM_util.cpp
1068 //