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