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