high frame rate stereo
[asdcplib.git] / src / KM_util.cpp
1 /*
2 Copyright (c) 2005-2011, 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 <KM_tai.h>
38 #include <KM_mutex.h>
39 #include <ctype.h>
40 #include <list>
41 #include <map>
42 #include <string>
43
44 const char*
45 Kumu::Version()
46 {
47   return PACKAGE_VERSION;
48 }
49
50
51 //------------------------------------------------------------------------------------------
52
53 // Result_t Internals
54
55 struct map_entry_t
56 {
57   int rcode;
58   Kumu::Result_t* result;
59 };
60
61
62 // WIN32 does not init this in time for use with Result_t(...) below, so it is
63 // now a pointer that Result_t(...) fills in when it needs it.
64 static Kumu::Mutex* s_MapLock = 0;
65
66 static ui32_t s_MapSize = 0;
67 static const ui32_t MapMax = 2048;
68 static struct map_entry_t s_ResultMap[MapMax];
69
70
71 //
72 const Kumu::Result_t&
73 Kumu::Result_t::Find(int v)
74 {
75   if ( v == 0 )
76     return RESULT_OK;
77
78   assert(s_MapLock);
79   AutoMutex L(*s_MapLock);
80
81   for ( ui32_t i = 0; i < s_MapSize; ++i )
82     {
83       if ( s_ResultMap[i].rcode == v )
84         return *s_ResultMap[i].result;
85     }
86
87   return RESULT_UNKNOWN;
88 }
89
90 //
91 Kumu::Result_t
92 Kumu::Result_t::Delete(int v)
93 {
94   if ( v < -99 || v > 99 )
95     {
96       DefaultLogSink().Error("Cannot delete core result code: %ld\n", v);
97       return RESULT_FAIL;
98     }
99
100   assert(s_MapLock);
101   AutoMutex L(*s_MapLock);
102
103   for ( ui32_t i = 0; i < s_MapSize; ++i )
104     {
105       if ( s_ResultMap[i].rcode == v )
106         {
107           for ( ++i; i < s_MapSize; ++i )
108             s_ResultMap[i-1] = s_ResultMap[i];
109
110           --s_MapSize;
111           return RESULT_OK;
112         }
113     }
114
115   return RESULT_FALSE;
116 }
117
118 //
119 unsigned int
120 Kumu::Result_t::End()
121 {
122   return s_MapSize;
123 }
124
125 //
126 const Kumu::Result_t&
127 Kumu::Result_t::Get(unsigned int i)
128 {
129   return *s_ResultMap[i].result;
130 }
131
132 //
133 Kumu::Result_t::Result_t(int v, const char* s, const char* l) : value(v), symbol(s), label(l)
134 {
135   assert(l);
136   assert(s);
137
138   if ( v == 0 )
139     return;
140
141   // This may seem tricky, but it is certain that the static values declared in KM_error.h will
142   // be created (and thus this method will be called) before main(...) is called.  It is not
143   // until then that threads could be created, thus the mutex will exist before multi-threaded
144   // access could occur.
145   if ( s_MapLock == 0 )
146     s_MapLock = new Kumu::Mutex;
147
148   assert(s_MapLock);
149   AutoMutex L(*s_MapLock);
150
151   for ( ui32_t i = 0; i < s_MapSize; ++i )
152     {
153       if ( s_ResultMap[i].rcode == v )
154         return;
155     }
156
157   assert(s_MapSize+1 < MapMax);
158
159   s_ResultMap[s_MapSize].rcode = v;
160   s_ResultMap[s_MapSize].result = this;
161   ++s_MapSize;
162
163   return;
164 }
165
166 Kumu::Result_t::~Result_t() {}
167
168
169 //------------------------------------------------------------------------------------------
170 // DTrace internals
171
172 static int s_DTraceSequence = 0;
173
174 Kumu::DTrace_t::DTrace_t(const char* Label, Kumu::Result_t* Watch, int Line, const char* File)
175   : m_Label(Label), m_Watch(Watch), m_Line(Line), m_File(File)
176 {
177   m_Sequence = s_DTraceSequence++;
178   DefaultLogSink().Debug("@enter %s[%d] (%s at %d)\n", m_Label, m_Sequence, m_File, m_Line);
179 }
180
181 Kumu::DTrace_t::~DTrace_t()
182 {
183   if ( m_Watch != 0  )
184     DefaultLogSink().Debug("@exit %s[%d]: %s\n", m_Label, m_Sequence, m_Watch->Label());
185   else
186     DefaultLogSink().Debug("@exit %s[%d]\n", m_Label, m_Sequence);
187 }
188
189 //------------------------------------------------------------------------------------------
190
191
192 const char  fill = '=';
193 const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
194
195 const byte_t decode_map[] =
196 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
197   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
198   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
199   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
200   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
201   0xff, 0xff, 0xff, 62,   0xff, 0xff, 0xff, 63,
202   52,   53,   54,   55,   56,   57,   58,   59,
203   60,   61,   0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
204   0xff, 0,    1,    2,    3,    4,    5,    6,
205   7,    8,    9,    10,   11,   12,   13,   14,
206   15,   16,   17,   18,   19,   20,   21,   22,
207   23,   24,   25,   0xff, 0xff, 0xff, 0xff, 0xff,
208   0xff, 26,   27,   28,   29,   30,   31,   32,
209   33,   34,   35,   36,   37,   38,   39,   40,
210   41,   42,   43,   44,   45,   46,   47,   48,
211   49,   50,   51,   0xff, 0xff, 0xff, 0xff, 0xff,
212   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
213   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
214   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
215   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
216   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
217   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
218   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
219   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
220   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
221   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
222   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
223   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
224   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
225   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
226   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
227   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
228 };
229
230
231 // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer
232 // if the binary buffer was large enough to hold the result. If the output buffer
233 // is too small or any of the pointer arguments are NULL, the subroutine will
234 // return 0.
235 //
236 const char*
237 Kumu::base64encode(const byte_t* buf, ui32_t buf_len, char* strbuf, ui32_t strbuf_len)
238 {
239   ui32_t out_char = 0;
240   ui32_t i, block_len, diff;
241
242   if ( buf == 0 || strbuf == 0 )
243     return 0;
244
245   if ( strbuf_len < base64_encode_length(buf_len) + 1 )
246     return 0;
247
248   block_len = buf_len;
249
250   while ( block_len % 3 )
251     block_len--;
252
253   for ( i = 0; i < block_len; i += 3 )
254     {
255       strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
256       strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
257       strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) | ( buf[2] >> 6 ) )];
258       strbuf[out_char++] = base64_chars[( buf[2] & 0x3f )];
259       buf += 3;
260     }
261
262   if ( i < buf_len )
263     {
264       diff = buf_len - i;
265       assert(diff > 0);
266       assert(diff < 3);
267       
268       strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
269
270       if ( diff == 1 )
271         {
272           strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) )];
273           strbuf[out_char++] = fill;
274         }
275       else if ( diff == 2 )
276         {
277           strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
278           strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) )];
279         }
280
281       strbuf[out_char++] = fill;
282     }
283
284   strbuf[out_char] = 0;
285   return strbuf;;
286 }
287
288
289
290
291 // Convert NULL-terminated UTF-8 Base64 string to binary, returns 0 if
292 // the binary buffer was large enough to hold the result. The output parameter
293 // 'char_count' will contain the length of the converted string. If the output
294 // buffer is too small or any of the pointer arguments are NULL, the subroutine
295 // will return -1 and set 'char_count' to the required buffer size. No data will
296 // be written to 'buf' if the subroutine fails.
297 //
298 i32_t
299 Kumu::base64decode(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count)
300 {
301   register byte_t c = 0, d = 0;
302   register ui32_t phase = 0, i = 0;
303
304   if ( str == 0 || buf == 0 || char_count == 0 )
305     return -1;
306
307   while ( *str != 0 && i < buf_len )
308     {
309       c = decode_map[(int)*str++];
310       if ( c == 0xff ) continue;
311       if ( c == 0xfe ) break;
312
313       switch ( phase++ )
314         {
315         case 0:
316           buf[i++] =  c << 2;
317           break;
318
319         case 1:
320           buf[i - 1] |= c >> 4;
321           d = c;
322           break;
323
324         case 2:
325           buf[i++] =  ( d << 4 ) | ( c >> 2 );
326           d = c;
327           break;
328
329         case 3:
330           buf[i++] =  ( d << 6 ) | c;
331           phase = 0;
332           break;
333         }
334     }
335
336   *char_count = i;
337   return 0;
338 }
339
340 //------------------------------------------------------------------------------------------
341
342 // convert utf-8 hext string to bin
343 i32_t
344 Kumu::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size)
345 {
346   KM_TEST_NULL_L(str);
347   KM_TEST_NULL_L(buf);
348   KM_TEST_NULL_L(conv_size);
349
350   *conv_size = 0;
351
352   if ( str[0] == 0 ) // nothing to convert
353     return 0;
354
355   for ( int j = 0; str[j]; j++ )
356     {
357       if ( isxdigit(str[j]) )
358         (*conv_size)++;
359     }
360
361   if ( *conv_size & 0x01 ) (*conv_size)++;
362   *conv_size /= 2;
363
364   if ( *conv_size > buf_len )// maximum possible data size
365     return -1;
366
367   *conv_size = 0;
368
369   int phase = 0; // track high/low nybble
370
371   // for each character, fill in the high nybble then the low
372   for ( int i = 0; str[i]; i++ )
373     {
374       if ( ! isxdigit(str[i]) )
375         continue;
376
377       byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) );
378
379       if ( phase == 0 )
380         {
381           buf[*conv_size] = val << 4;
382           phase++;
383         }
384       else
385         {
386           buf[*conv_size] |= val;
387           phase = 0;
388           (*conv_size)++;
389         }
390     }
391
392   return 0;
393 }
394
395 #ifdef CONFIG_RANDOM_UUID
396
397 // convert a memory region to a NULL-terminated hexadecimal string
398 //
399 static const char*
400 bin2hex_rand(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
401 {
402   if ( bin_buf == 0
403        || str_buf == 0
404        || ((bin_len * 2) + 1) > str_len )
405     return 0;
406
407   char* p = str_buf;
408   Kumu::mem_ptr<byte_t> rand_buf = new byte_t[bin_len];
409   Kumu::FortunaRNG RNG;
410   RNG.FillRandom(rand_buf, bin_len);
411
412   for ( ui32_t i = 0; i < bin_len; i++ )
413     {
414       *p = (bin_buf[i] >> 4) & 0x0f;
415       *p += *p < 10 ? 0x30 : (( ((rand_buf[i] & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
416       p++;
417
418       *p = bin_buf[i] & 0x0f;
419       *p += *p < 10 ? 0x30 : (( (((rand_buf[i] >> 1) & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
420       p++;
421     }
422
423   *p = '\0';
424   return str_buf;
425 }
426 #endif
427
428 // convert a memory region to a NULL-terminated hexadecimal string
429 //
430 const char*
431 Kumu::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
432 {
433   if ( bin_buf == 0
434        || str_buf == 0
435        || ((bin_len * 2) + 1) > str_len )
436     return 0;
437
438 #ifdef CONFIG_RANDOM_UUID
439   const char* use_random_uuid  = getenv("KM_USE_RANDOM_UUID");
440   if ( use_random_uuid != 0 && use_random_uuid[0] != 0 && use_random_uuid[0] != '0' )
441     return bin2hex_rand(bin_buf, bin_len, str_buf, str_len);
442 #endif
443
444   char* p = str_buf;
445
446   for ( ui32_t i = 0; i < bin_len; i++ )
447     {
448       *p = (bin_buf[i] >> 4) & 0x0f;
449       *p += *p < 10 ? 0x30 : 0x61 - 10;
450       p++;
451
452       *p = bin_buf[i] & 0x0f;
453       *p += *p < 10 ? 0x30 : 0x61 - 10;
454       p++;
455     }
456
457   *p = '\0';
458   return str_buf;
459 }
460
461
462 // spew a range of bin data as hex
463 void
464 Kumu::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream)
465 {
466   if ( buf == 0 )
467     return;
468
469   if ( stream == 0 )
470     stream = stderr;
471
472   static ui32_t row_len = 16;
473   const byte_t* p = buf;
474   const byte_t* end_p = p + dump_len;
475
476   for ( ui32_t line = 0; p < end_p; line++ )
477     {
478       fprintf(stream, "  %06x: ", line);
479       ui32_t i;
480       const byte_t* pp;
481
482       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
483         fprintf(stream, "%02x ", *pp);
484
485       while ( i++ < row_len )
486         fputs("   ", stream);
487
488       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
489         fputc((isprint(*pp) ? *pp : '.'), stream);
490
491       fputc('\n', stream);
492       p += row_len;
493     }
494 }
495
496 //
497 const char*
498 Kumu::bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
499 {
500   ui32_t i, j, k;
501
502   if ( str_len < 34 || bin_len != UUID_Length )
503     return 0;
504
505   if ( bin2hex(bin_buf, bin_len, str_buf, str_len) == 0 )
506     return 0;
507
508   // shift the node id
509   for ( k = 19, i = 12; i > 0; i-- )
510     str_buf[k+i+4] = str_buf[k+i];
511
512   // shift the time (mid+hi+clk)
513   for ( k = 15, j = 3; k > 6; k -= 4, j-- )
514     {
515       for ( i = 4; i > 0; i-- )
516         str_buf[k+i+j] = str_buf[k+i];
517     }
518
519   // add in the hyphens and trainling null
520   for ( i = 8; i < 24; i += 5 )
521     str_buf[i] = '-';
522   
523   str_buf[36] = 0;
524   return str_buf;
525 }
526
527 //
528 void
529 Kumu::GenRandomValue(UUID& ID)
530 {
531   byte_t tmp_buf[UUID_Length];
532   GenRandomUUID(tmp_buf);
533   ID.Set(tmp_buf);
534 }
535
536 //
537 void
538 Kumu::GenRandomUUID(byte_t* buf)
539 {
540   FortunaRNG RNG;
541   RNG.FillRandom(buf, UUID_Length);
542   buf[6] &= 0x0f; // clear bits 4-7
543   buf[6] |= 0x40; // set UUID version
544   buf[8] &= 0x3f; // clear bits 6&7
545   buf[8] |= 0x80; // set bit 7
546 }
547
548 //
549 void
550 Kumu::GenRandomValue(SymmetricKey& Key)
551 {
552   byte_t tmp_buf[SymmetricKey_Length];
553   FortunaRNG RNG;
554   RNG.FillRandom(tmp_buf, SymmetricKey_Length);
555   Key.Set(tmp_buf);
556 }
557
558
559 //------------------------------------------------------------------------------------------
560 // read a ber value from the buffer and compare with test value.
561 // Advances buffer to first character after BER value.
562 //
563 bool
564 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
565 {
566   if ( buf == 0 )
567     return false;
568
569   if ( ( **buf & 0x80 ) == 0 )
570     return false;
571
572   ui64_t val = 0;
573   ui8_t ber_size = ( **buf & 0x0f ) + 1;
574
575   if ( ber_size > 9 )
576     return false;
577
578   for ( ui8_t i = 1; i < ber_size; i++ )
579     {
580       if ( (*buf)[i] > 0 )
581         val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
582     }
583
584   *buf += ber_size;
585   return ( val == test_value );
586 }
587
588
589 //
590 bool
591 Kumu::read_BER(const byte_t* buf, ui64_t* val)
592 {
593   ui8_t ber_size, i;
594   
595   if ( buf == 0 || val == 0 )
596     return false;
597
598   if ( ( *buf & 0x80 ) == 0 )
599     return false;
600
601   *val = 0;
602   ber_size = ( *buf & 0x0f ) + 1;
603
604   if ( ber_size > 9 )
605     return false;
606
607   for ( i = 1; i < ber_size; i++ )
608     {
609       if ( buf[i] > 0 )
610         *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
611     }
612
613   return true;
614 }
615
616
617 static const ui64_t ber_masks[9] =
618   { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), 
619     ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
620     ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
621     ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
622     0
623   };
624
625 //
626 ui32_t
627 Kumu::get_BER_length_for_value(ui64_t val)
628 {
629   for ( ui32_t i = 0; i < 9; i++ )
630     {
631       if ( ( val & ber_masks[i] ) == 0 )
632         return i + 1;
633     }
634
635   ui64Printer tmp_i(val);
636   DefaultLogSink().Error("BER integer encoding not supported for large value %s\n", tmp_i.c_str());
637   return 0;
638 }
639
640 //
641 bool
642 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
643 {
644   if ( buf == 0 )
645     return false;
646
647   if ( ber_len == 0 )
648     { // calculate default length
649       if ( val < 0x01000000L )
650         ber_len = 4;
651       else if ( val < ui64_C(0x0100000000000000) )
652         ber_len = 8;
653       else
654         ber_len = 9;
655     }
656   else
657     { // sanity check BER length
658       if ( ber_len > 9 )
659         {
660           DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len);
661           return false;
662         }
663       
664       if ( ( val & ber_masks[ber_len - 1] ) != 0 )
665         {
666           ui64Printer tmp_i(val);
667           DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str());
668           return false;
669         }
670     }
671
672   buf[0] = 0x80 + ( ber_len - 1 );
673
674   for ( ui32_t i = ber_len - 1; i > 0; i-- )
675     {
676       buf[i] = (ui8_t)(val & 0xff);
677       val >>= 8;
678     }
679
680   return true;
681 }
682
683
684 //------------------------------------------------------------------------------------------
685 #ifdef KM_WIN32
686
687 #define TIMESTAMP_TO_SYSTIME(ts, t) \
688   (t)->wYear    = (ts).Year;   /* year */ \
689   (t)->wMonth   = (ts).Month;  /* month of year (1 - 12) */ \
690   (t)->wDay     = (ts).Day;    /* day of month (1 - 31) */ \
691   (t)->wHour    = (ts).Hour;   /* hours (0 - 23) */ \
692   (t)->wMinute  = (ts).Minute; /* minutes (0 - 59) */ \
693   (t)->wSecond  = (ts).Second; /* seconds (0 - 60) */ \
694   (t)->wDayOfWeek = 0; \
695   (t)->wMilliseconds = 0
696
697 #define SYSTIME_TO_TIMESTAMP(t, ts) \
698   (ts).Year   = (t)->wYear;    /* year */ \
699   (ts).Month  = (t)->wMonth;   /* month of year (1 - 12) */ \
700   (ts).Day    = (t)->wDay;     /* day of month (1 - 31) */ \
701   (ts).Hour   = (t)->wHour;    /* hours (0 - 23) */ \
702   (ts).Minute = (t)->wMinute;  /* minutes (0 - 59) */ \
703   (ts).Second = (t)->wSecond;  /* seconds (0 - 60) */
704
705 //
706 Kumu::Timestamp::Timestamp() :
707   Year(0), Month(0),  Day(0), Hour(0), Minute(0), Second(0)
708 {
709   SYSTEMTIME sys_time;
710   GetSystemTime(&sys_time);
711   SYSTIME_TO_TIMESTAMP(&sys_time, *this);
712 }
713
714 //
715 bool
716 Kumu::Timestamp::operator<(const Timestamp& rhs) const
717 {
718   SYSTEMTIME lhst, rhst;
719   FILETIME lft, rft;
720
721   TIMESTAMP_TO_SYSTIME(*this, &lhst);
722   TIMESTAMP_TO_SYSTIME(rhs, &rhst);
723   SystemTimeToFileTime(&lhst, &lft);
724   SystemTimeToFileTime(&rhst, &rft);
725   return ( CompareFileTime(&lft, &rft) == -1 );
726 }
727
728 //
729 bool
730 Kumu::Timestamp::operator>(const Timestamp& rhs) const
731 {
732   SYSTEMTIME lhst, rhst;
733   FILETIME lft, rft;
734
735   TIMESTAMP_TO_SYSTIME(*this, &lhst);
736   TIMESTAMP_TO_SYSTIME(rhs, &rhst);
737   SystemTimeToFileTime(&lhst, &lft);
738   SystemTimeToFileTime(&rhst, &rft);
739   return ( CompareFileTime(&lft, &rft) == 1 );
740 }
741
742 inline ui64_t
743 seconds_to_ns100(ui32_t seconds)
744 {
745   return ((ui64_t)seconds * 10000000);
746 }
747
748 //
749 void
750 Kumu::Timestamp::AddDays(i32_t days)
751 {
752   SYSTEMTIME current_st;
753   FILETIME current_ft;
754   ULARGE_INTEGER current_ul;
755
756   if ( days != 0 )
757     {
758       TIMESTAMP_TO_SYSTIME(*this, &current_st);
759       SystemTimeToFileTime(&current_st, &current_ft);
760       memcpy(&current_ul, &current_ft, sizeof(current_ul));
761       current_ul.QuadPart += ( seconds_to_ns100(86400) * (i64_t)days );
762       memcpy(&current_ft, &current_ul, sizeof(current_ft));
763       FileTimeToSystemTime(&current_ft, &current_st);
764       SYSTIME_TO_TIMESTAMP(&current_st, *this);
765     }
766 }
767
768 //
769 void
770 Kumu::Timestamp::AddHours(i32_t hours)
771 {
772   SYSTEMTIME current_st;
773   FILETIME current_ft;
774   ULARGE_INTEGER current_ul;
775
776   if ( hours != 0 )
777     {
778       TIMESTAMP_TO_SYSTIME(*this, &current_st);
779       SystemTimeToFileTime(&current_st, &current_ft);
780       memcpy(&current_ul, &current_ft, sizeof(current_ul));
781       current_ul.QuadPart += ( seconds_to_ns100(3600) * (i64_t)hours );
782       memcpy(&current_ft, &current_ul, sizeof(current_ft));
783       FileTimeToSystemTime(&current_ft, &current_st);
784       SYSTIME_TO_TIMESTAMP(&current_st, *this);
785     }
786 }
787
788 //
789 void
790 Kumu::Timestamp::AddMinutes(i32_t minutes)
791 {
792   SYSTEMTIME current_st;
793   FILETIME current_ft;
794   ULARGE_INTEGER current_ul;
795
796   if ( minutes != 0 )
797     {
798       TIMESTAMP_TO_SYSTIME(*this, &current_st);
799       SystemTimeToFileTime(&current_st, &current_ft);
800       memcpy(&current_ul, &current_ft, sizeof(current_ul));
801       current_ul.QuadPart += ( seconds_to_ns100(60) * (i64_t)minutes );
802       memcpy(&current_ft, &current_ul, sizeof(current_ft));
803       FileTimeToSystemTime(&current_ft, &current_st);
804       SYSTIME_TO_TIMESTAMP(&current_st, *this);
805     }
806 }
807
808 //
809 void
810 Kumu::Timestamp::AddSeconds(i32_t seconds)
811 {
812   SYSTEMTIME current_st;
813   FILETIME current_ft;
814   ULARGE_INTEGER current_ul;
815
816   if ( seconds != 0 )
817     {
818       TIMESTAMP_TO_SYSTIME(*this, &current_st);
819       SystemTimeToFileTime(&current_st, &current_ft);
820       memcpy(&current_ul, &current_ft, sizeof(current_ul));
821       current_ul.QuadPart += ( seconds_to_ns100(1) * (i64_t)seconds );
822       memcpy(&current_ft, &current_ul, sizeof(current_ft));
823       FileTimeToSystemTime(&current_ft, &current_st);
824       SYSTIME_TO_TIMESTAMP(&current_st, *this);
825     }
826 }
827
828 #else // KM_WIN32
829
830 #include <time.h>
831
832 #define TIMESTAMP_TO_CALTIME(ts, ct)                                   \
833   (ct)->date.year  = (ts).Year;    /* year */                          \
834   (ct)->date.month = (ts).Month;   /* month of year (1 - 12) */        \
835   (ct)->date.day   = (ts).Day;     /* day of month (1 - 31) */         \
836   (ct)->hour       = (ts).Hour;    /* hours (0 - 23) */                \
837   (ct)->minute     = (ts).Minute;  /* minutes (0 - 59) */              \
838   (ct)->second     = (ts).Second;  /* seconds (0 - 60) */              \
839   (ct)->offset     = 0;
840
841 #define CALTIME_TO_TIMESTAMP(ct, ts)                                   \
842   assert((ct)->offset == 0);                                           \
843   (ts).Year   = (ct)->date.year;    /* year */                         \
844   (ts).Month  = (ct)->date.month;   /* month of year (1 - 12) */       \
845   (ts).Day    = (ct)->date.day;     /* day of month (1 - 31) */        \
846   (ts).Hour   = (ct)->hour;         /* hours (0 - 23) */               \
847   (ts).Minute = (ct)->minute;       /* minutes (0 - 59) */             \
848   (ts).Second = (ct)->second;       /* seconds (0 - 60) */
849
850
851 //
852 Kumu::Timestamp::Timestamp() :
853   Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
854 {
855   Kumu::TAI::tai now;
856   Kumu::TAI::caltime ct;
857   now.now();
858   ct = now;
859   CALTIME_TO_TIMESTAMP(&ct, *this)
860 }
861
862 //
863 bool
864 Kumu::Timestamp::operator<(const Timestamp& rhs) const
865 {
866   Kumu::TAI::caltime lh_ct, rh_ct;
867   TIMESTAMP_TO_CALTIME(*this, &lh_ct)
868   TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
869
870   Kumu::TAI::tai lh_tai, rh_tai;
871   lh_tai = lh_ct;
872   rh_tai = rh_ct;
873
874   return ( lh_tai.x < rh_tai.x );
875 }
876
877 //
878 bool
879 Kumu::Timestamp::operator>(const Timestamp& rhs) const
880 {
881   Kumu::TAI::caltime lh_ct, rh_ct;
882   TIMESTAMP_TO_CALTIME(*this, &lh_ct)
883   TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
884
885   Kumu::TAI::tai lh_tai, rh_tai;
886   lh_tai = lh_ct;
887   rh_tai = rh_ct;
888
889   return ( lh_tai.x > rh_tai.x );
890 }
891
892 //
893 void
894 Kumu::Timestamp::AddDays(i32_t days)
895 {
896   Kumu::TAI::caltime ct;
897   Kumu::TAI::tai t;
898
899   if ( days != 0 )
900     {
901       TIMESTAMP_TO_CALTIME(*this, &ct)
902       t = ct;
903       t.add_days(days);
904       ct = t;
905       CALTIME_TO_TIMESTAMP(&ct, *this)
906     }
907 }
908
909 //
910 void
911 Kumu::Timestamp::AddHours(i32_t hours)
912 {
913   Kumu::TAI::caltime ct;
914   Kumu::TAI::tai t;
915
916   if ( hours != 0 )
917     {
918       TIMESTAMP_TO_CALTIME(*this, &ct)
919       t = ct;
920       t.add_hours(hours);
921       ct = t;
922       CALTIME_TO_TIMESTAMP(&ct, *this)
923     }
924 }
925
926 //
927 void
928 Kumu::Timestamp::AddMinutes(i32_t minutes)
929 {
930   Kumu::TAI::caltime ct;
931   Kumu::TAI::tai t;
932
933   if ( minutes != 0 )
934     {
935       TIMESTAMP_TO_CALTIME(*this, &ct)
936       t = ct;
937       t.add_minutes(minutes);
938       ct = t;
939       CALTIME_TO_TIMESTAMP(&ct, *this)
940     }
941 }
942
943 //
944 void
945 Kumu::Timestamp::AddSeconds(i32_t seconds)
946 {
947   Kumu::TAI::caltime ct;
948   Kumu::TAI::tai t;
949
950   if ( seconds != 0 )
951     {
952       TIMESTAMP_TO_CALTIME(*this, &ct)
953       t = ct;
954       t.add_seconds(seconds);
955       ct = t;
956       CALTIME_TO_TIMESTAMP(&ct, *this)
957     }
958 }
959
960 #endif // KM_WIN32
961
962
963 Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
964 {
965   Year   = rhs.Year;
966   Month  = rhs.Month;
967   Day    = rhs.Day;
968   Hour   = rhs.Hour;
969   Minute = rhs.Minute;
970   Second = rhs.Second;
971 }
972
973 //
974 Kumu::Timestamp::Timestamp(const char* datestr) : IArchive()
975 {
976   if ( ! DecodeString(datestr) )
977     {
978       *this = Timestamp();
979     }
980 }
981
982 //
983 Kumu::Timestamp::~Timestamp()
984 {
985 }
986
987 //
988 const Kumu::Timestamp&
989 Kumu::Timestamp::operator=(const Timestamp& rhs)
990 {
991   Year   = rhs.Year;
992   Month  = rhs.Month;
993   Day    = rhs.Day;
994   Hour   = rhs.Hour;
995   Minute = rhs.Minute;
996   Second = rhs.Second;
997   return *this;
998 }
999
1000 //
1001 bool
1002 Kumu::Timestamp::operator==(const Timestamp& rhs) const
1003 {
1004   if ( Year == rhs.Year
1005        && Month  == rhs.Month
1006        && Day    == rhs.Day
1007        && Hour   == rhs.Hour
1008        && Minute == rhs.Minute
1009        && Second == rhs.Second )
1010     return true;
1011
1012   return false;
1013 }
1014
1015 //
1016 bool
1017 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
1018 {
1019   if ( Year != rhs.Year
1020        || Month  != rhs.Month
1021        || Day    != rhs.Day
1022        || Hour   != rhs.Hour
1023        || Minute != rhs.Minute
1024        || Second != rhs.Second )
1025     return true;
1026
1027   return false;
1028 }
1029
1030 // 
1031 const char*
1032 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
1033 {
1034   return EncodeStringWithOffset(str_buf, buf_len, 0);
1035 }
1036
1037 // 
1038 const char*
1039 Kumu::Timestamp::EncodeStringWithOffset(char* str_buf, ui32_t buf_len,
1040                                         i32_t offset_minutes) const
1041 {
1042   if ( buf_len < ( DateTimeLen + 1 ) )
1043     return 0;
1044
1045   // ensure offset is within +/- 14 hours
1046   if ((offset_minutes < -14 * 60) || (offset_minutes > 14 * 60))
1047     return 0;
1048
1049   // set the apparent time
1050   Kumu::Timestamp tmp_t(*this);
1051   tmp_t.AddMinutes(offset_minutes);
1052
1053   char direction = '+';
1054   if (offset_minutes < 0) {
1055     direction = '-';
1056     // need absolute offset from zero
1057     offset_minutes = -offset_minutes;
1058   }
1059
1060   // 2004-05-01T13:20:00+00:00
1061   snprintf(str_buf, buf_len,
1062            "%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02hu:%02hu",
1063            tmp_t.Year, tmp_t.Month, tmp_t.Day,
1064            tmp_t.Hour, tmp_t.Minute, tmp_t.Second,
1065            direction,
1066            offset_minutes / 60,
1067            offset_minutes % 60);
1068
1069   return str_buf;
1070 }
1071
1072 //
1073 bool
1074 Kumu::Timestamp::DecodeString(const char* datestr)
1075 {
1076   Timestamp TmpStamp;
1077
1078   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
1079        || datestr[4] != '-'
1080        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
1081        || datestr[7] != '-'
1082        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
1083     return false;
1084
1085   ui32_t char_count = 10;
1086   TmpStamp.Year = atoi(datestr);
1087   TmpStamp.Month = atoi(datestr + 5);
1088   TmpStamp.Day = atoi(datestr + 8);
1089   TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
1090  
1091   if ( datestr[10] == 'T' )
1092     {
1093       if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
1094            || datestr[13] != ':'
1095            || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
1096         return false;
1097
1098       char_count += 6;
1099       TmpStamp.Hour = atoi(datestr + 11);
1100       TmpStamp.Minute = atoi(datestr + 14);
1101
1102       if ( datestr[16] == ':' )
1103         {
1104           if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
1105             return false;
1106
1107           char_count += 3;
1108           TmpStamp.Second = atoi(datestr + 17);
1109         }
1110
1111       if ( datestr[19] == '.' )
1112         {
1113           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
1114             return false;
1115           
1116           // we don't carry the ms value
1117           datestr += 4;
1118         }
1119
1120       if ( datestr[19] == '-' || datestr[19] == '+' )
1121         {
1122           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
1123                || datestr[22] != ':'
1124                || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
1125             return false;
1126
1127           char_count += 6;
1128
1129           ui32_t TZ_hh = atoi(datestr + 20);
1130           ui32_t TZ_mm = atoi(datestr + 23);
1131           if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
1132             return false;
1133
1134           i32_t TZ_offset = 60 * TZ_hh + TZ_mm;
1135           if (datestr[19] == '-')
1136             TZ_offset = -TZ_offset;
1137           /* at this point, TZ_offset reflects the contents of the string */
1138
1139           /* a negative offset is behind UTC and so needs to increment to
1140            * convert, while a positive offset must do the reverse */
1141           TmpStamp.AddMinutes(-TZ_offset);
1142         }
1143       else if (datestr[19] == 'Z')
1144         {
1145           /* act as if the offset were +00:00 */
1146           char_count++;
1147         }
1148     }
1149
1150   if ( datestr[char_count] != 0 )
1151     {
1152       Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
1153                                    datestr, char_count);
1154       return false;
1155     }
1156
1157 #ifdef KM_WIN32
1158   SYSTEMTIME st;
1159   FILETIME ft;
1160   TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
1161   if ( SystemTimeToFileTime(&st, &ft) == 0 )
1162     return false;
1163   SYSTIME_TO_TIMESTAMP(&st, *this);
1164 #else
1165   Kumu::TAI::tai t;
1166   Kumu::TAI::caltime ct;
1167   TIMESTAMP_TO_CALTIME(TmpStamp, &ct);
1168   t = ct; // back and forth to tai to normalize offset
1169   ct = t;
1170   CALTIME_TO_TIMESTAMP(&ct, *this)
1171 #endif
1172
1173   return true;
1174 }
1175
1176 //
1177 bool
1178 Kumu::Timestamp::HasValue() const
1179 {
1180   if ( Year || Month || Day || Hour || Minute || Second )
1181     return true;
1182
1183   return false;
1184 }
1185
1186 //
1187 bool
1188 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
1189 {
1190   assert(Reader);
1191   if ( ! Reader->ReadUi16BE(&Year) ) return false;      
1192   if ( ! Reader->ReadRaw(&Month, 6) ) return false;
1193   return true;
1194 }
1195
1196 //
1197 bool
1198 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
1199 {
1200   assert(Writer);
1201   if ( ! Writer->WriteUi16BE(Year) ) return false;      
1202   if ( ! Writer->WriteRaw(&Month, 6) ) return false;
1203   return true;
1204 }
1205
1206 //
1207 long
1208 Kumu::Timestamp::GetSecondsSinceEpoch(void) const
1209 {
1210 #ifdef KM_WIN32
1211   SYSTEMTIME timeST;
1212   TIMESTAMP_TO_SYSTIME(*this, &timeST);
1213   FILETIME timeFT;
1214   SystemTimeToFileTime(&timeST, &timeFT);
1215   ULARGE_INTEGER timeUL;
1216   timeUL.LowPart = timeFT.dwLowDateTime;
1217   timeUL.HighPart = timeFT.dwHighDateTime;
1218
1219   SYSTEMTIME epochST;
1220   epochST.wYear = 1970;
1221   epochST.wMonth = 0;
1222   epochST.wDayOfWeek = 4;
1223   epochST.wDay = 1;
1224   epochST.wHour = 0;
1225   epochST.wMinute = 0;
1226   epochST.wSecond = 0;
1227   epochST.wMilliseconds = 0;
1228   FILETIME epochFT;
1229   SystemTimeToFileTime(&epochST, &epochFT);
1230   ULARGE_INTEGER epochUL;
1231   epochUL.LowPart = epochFT.dwLowDateTime;
1232   epochUL.HighPart = epochFT.dwHighDateTime;
1233
1234   return (timeUL.QuadPart - epochUL.QuadPart) / 10000000;
1235 #else
1236   Kumu::TAI::caltime ct;
1237   Kumu::TAI::tai t;
1238   TIMESTAMP_TO_CALTIME(*this, &ct);
1239   t = ct;
1240
1241   return (long) (t.x - ui64_C(4611686018427387914));
1242 #endif
1243 }
1244
1245 //------------------------------------------------------------------------------------------
1246
1247 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
1248   : m_p(0), m_capacity(0), m_size(0)
1249 {
1250   m_p = Buf->Data();
1251   m_capacity = Buf->Capacity();
1252   assert(m_p); assert(m_capacity);
1253 }
1254
1255 bool
1256 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
1257 {
1258   if ( ( m_size + ber_len ) > m_capacity )
1259     return false;
1260
1261   if ( ! write_BER(m_p + m_size, i, ber_len) )
1262     return false;
1263
1264   m_size += ber_len;
1265   return true;
1266 }
1267
1268
1269 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1270   : m_p(0), m_capacity(0), m_size(0)
1271 {
1272   m_p = Buf->RoData();
1273   m_capacity = Buf->Length();
1274   assert(m_p); assert(m_capacity);
1275 }
1276
1277 bool
1278 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1279 {
1280   if ( i == 0 || ber_len == 0 ) return false;
1281
1282   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1283     return false;
1284
1285   if ( ( m_size + *ber_len ) > m_capacity )
1286     return false;
1287
1288   if ( ! read_BER(m_p + m_size, i) )
1289     return false;
1290
1291   m_size += *ber_len;
1292   return true;
1293 }
1294
1295 //------------------------------------------------------------------------------------------
1296
1297 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1298
1299 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1300 {
1301   Capacity(cap);
1302 }
1303
1304 Kumu::ByteString::~ByteString()
1305 {
1306   if ( m_Data != 0 )
1307     free(m_Data);
1308 }
1309
1310
1311 // copy the given data into the ByteString, set Length value.
1312 // Returns error if the ByteString is too small.
1313 Kumu::Result_t
1314 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1315 {
1316   if ( m_Capacity < buf_len )
1317     return RESULT_ALLOC;
1318
1319   memcpy(m_Data, buf, buf_len);
1320   m_Length = buf_len;
1321   return RESULT_OK;
1322 }
1323
1324
1325 // copy the given data into the ByteString, set Length value.
1326 // Returns error if the ByteString is too small.
1327 Kumu::Result_t
1328 Kumu::ByteString::Set(const ByteString& Buf)
1329 {
1330   if ( m_Capacity < Buf.m_Capacity )
1331     return RESULT_ALLOC;
1332
1333   memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1334   m_Length = Buf.m_Length;
1335   return RESULT_OK;
1336 }
1337
1338
1339 // Sets the size of the internally allocate buffer.
1340 Kumu::Result_t
1341 Kumu::ByteString::Capacity(ui32_t cap_size)
1342 {
1343   if ( m_Capacity >= cap_size )
1344     return RESULT_OK;
1345
1346   byte_t* tmp_data = 0;
1347   if ( m_Data != 0 )
1348     {
1349       if ( m_Length > 0 )
1350         tmp_data = m_Data;
1351       else
1352         free(m_Data);
1353     }
1354                 
1355   if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1356     return RESULT_ALLOC;
1357
1358   if ( tmp_data != 0 )
1359     {
1360       assert(m_Length > 0);
1361       memcpy(m_Data, tmp_data, m_Length);
1362       free(tmp_data);
1363     }
1364                 
1365   m_Capacity = cap_size;
1366   return RESULT_OK;
1367 }
1368
1369 //
1370 Kumu::Result_t
1371 Kumu::ByteString::Append(const ByteString& Buf)
1372 {
1373   Result_t result = RESULT_OK;
1374   ui32_t diff = m_Capacity - m_Length;
1375
1376   if ( diff < Buf.Length() )
1377     result = Capacity(m_Capacity + Buf.Length());
1378
1379   if ( KM_SUCCESS(result) )
1380     {
1381       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1382       m_Length += Buf.Length();
1383     }
1384
1385   return result;
1386 }
1387
1388 //
1389 Kumu::Result_t
1390 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1391 {
1392   Result_t result = RESULT_OK;
1393   ui32_t diff = m_Capacity - m_Length;
1394
1395   if ( diff < buf_len )
1396     result = Capacity(m_Capacity + buf_len);
1397
1398   if ( KM_SUCCESS(result) )
1399     {
1400       memcpy(m_Data + m_Length, buf, buf_len);
1401       m_Length += buf_len;
1402     }
1403
1404   return result;
1405 }
1406
1407
1408 //
1409 // end KM_util.cpp
1410 //