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