Work!
[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& ID)
437 {
438   byte_t tmp_buf[SymmetricKey_Length];
439   FortunaRNG RNG;
440   RNG.FillRandom(tmp_buf, SymmetricKey_Length);
441   ID.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 %lu 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 %lu 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   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
800        || datestr[4] != '-'
801        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
802        || datestr[7] != '-'
803        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) )
804        || datestr[10] != 'T'
805        || ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
806        || datestr[13] != ':'
807        || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) )
808        || datestr[16] != ':'
809        || ! ( isdigit(datestr[17]) && isdigit(datestr[18]) )
810        || ! ( datestr[19] == '-' || datestr[19] == '+' )
811        || ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
812        || datestr[22] != ':'
813        || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
814     return false;
815
816   // TODO -- test this!
817   Year = atoi(datestr);
818   Month = atoi(datestr + 5);
819   Day = atoi(datestr + 8);
820   Hour = atoi(datestr + 11);
821   Minute = atoi(datestr + 14);
822   Second = atoi(datestr + 17);
823
824   ui32_t TZ_hh = atoi(datestr + 20);
825   ui32_t TZ_mm = atoi(datestr + 23);
826
827   if ( TZ_mm != 0 )
828     DefaultLogSink().Error("Ignoring sub-hours timezone offset: %lu\n", TZ_mm);
829  
830   if ( TZ_hh > 12 )
831     DefaultLogSink().Error("Ignoring large timezone offset: %s\n", (datestr+19));
832   else 
833     AddHours(TZ_hh);
834
835   return true;
836 }
837
838 //
839 bool
840 Kumu::Timestamp::HasValue() const
841 {
842   if ( Year || Month || Day || Hour || Minute || Second )
843     return true;
844
845   return false;
846 }
847
848 //
849 bool
850 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
851 {
852   assert(Reader);
853   if ( ! Reader->ReadUi16BE(&Year) ) return false;      
854   if ( ! Reader->ReadRaw(&Month, 6) ) return false;
855   return true;
856 }
857
858 //
859 bool
860 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
861 {
862   assert(Writer);
863   if ( ! Writer->WriteUi16BE(Year) ) return false;      
864   if ( ! Writer->WriteRaw(&Month, 6) ) return false;
865   return true;
866 }
867
868 #if 0
869 //
870 bool
871 Kumu::UnarchiveString(MemIOReader* Reader, std::string&)
872 {
873   assert(Reader);
874   ui32_t str_length;
875   if ( ! Reader->ReadUi32BE(&str_length) ) return false;
876   assign((const char*)Reader->CurrentData(), str_length);
877   if ( ! Reader->SkipOffset(str_length) ) return false;
878   return true;
879 }
880
881 //
882 bool
883 Kumu::String::Archive(MemIOWriter* Writer) const
884 {
885   assert(Writer);
886   if ( ! Writer->WriteUi32BE(length()) ) return false;
887   if ( ! Writer->WriteRaw((const byte_t*)c_str(), length()) ) return false;
888
889   return true;
890 }
891 #endif
892
893 //------------------------------------------------------------------------------------------
894
895 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
896   : m_p(0), m_capacity(0), m_size(0)
897 {
898   m_p = Buf->Data();
899   m_capacity = Buf->Capacity();
900   assert(m_p); assert(m_capacity);
901 }
902
903 bool
904 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
905 {
906   if ( ( m_size + ber_len ) > m_capacity )
907     return false;
908
909   if ( ! write_BER(m_p + m_size, i, ber_len) )
910     return false;
911
912   m_size += ber_len;
913   return true;
914 }
915
916
917 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
918   : m_p(0), m_capacity(0), m_size(0)
919 {
920   m_p = Buf->RoData();
921   m_capacity = Buf->Capacity();
922   assert(m_p); assert(m_capacity);
923 }
924
925 bool
926 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
927 {
928   if ( i == 0 || ber_len == 0 ) return false;
929
930   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
931     return false;
932
933   if ( ( m_size + *ber_len ) > m_capacity )
934     return false;
935
936   if ( ! read_BER(m_p + m_size, i) )
937     return false;
938
939   m_size += *ber_len;
940   return true;
941 }
942
943 //------------------------------------------------------------------------------------------
944
945 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
946
947 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
948 {
949   Capacity(cap);
950 }
951
952 Kumu::ByteString::~ByteString()
953 {
954   if ( m_Data != 0 )
955     free(m_Data);
956 }
957
958
959 // copy the given data into the ByteString, set Length value.
960 // Returns error if the ByteString is too small.
961 Kumu::Result_t
962 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
963 {
964   if ( m_Capacity < buf_len )
965     return RESULT_ALLOC;
966
967   memcpy(m_Data, buf, buf_len);
968   m_Length = buf_len;
969   return RESULT_OK;
970 }
971
972
973 // Sets the size of the internally allocate buffer.
974 // Resets content length to zero.
975 Kumu::Result_t
976 Kumu::ByteString::Capacity(ui32_t cap_size)
977 {
978   if ( m_Capacity < cap_size )
979     {
980       if ( m_Data != 0 )
981         free(m_Data);
982                 
983       m_Data = (byte_t*)malloc(cap_size);
984                 
985       if ( m_Data == 0 )
986         return RESULT_ALLOC;
987                 
988       m_Capacity = cap_size;
989       m_Length = 0;
990     }
991         
992   return RESULT_OK;
993 }
994
995 //
996 Kumu::Result_t
997 Kumu::ByteString::Append(const ByteString& Buf)
998 {
999   Result_t result = RESULT_OK;
1000   ui32_t diff = m_Capacity - m_Length;
1001
1002   if ( diff < Buf.Length() )
1003     result = Capacity(m_Capacity + Buf.Length());
1004
1005   if ( KM_SUCCESS(result) )
1006     {
1007       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1008       m_Length += Buf.Length();
1009     }
1010
1011   return result;
1012 }
1013
1014 //
1015 Kumu::Result_t
1016 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1017 {
1018   Result_t result = RESULT_OK;
1019   ui32_t diff = m_Capacity - m_Length;
1020
1021   if ( diff < buf_len )
1022     result = Capacity(m_Capacity + buf_len);
1023
1024   if ( KM_SUCCESS(result) )
1025     {
1026       memcpy(m_Data + m_Length, buf, buf_len);
1027       m_Length += buf_len;
1028     }
1029
1030   return result;
1031 }
1032
1033
1034 //
1035 // end KM_util.cpp
1036 //