Basics of fake write seem to work.
[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 #ifdef LIBDCP_POSIX
538 void
539 Kumu::ResetTestRNG()
540 {
541   FortunaRNG RNG;
542   RNG.Reset();
543 }
544 #endif
545
546 //
547 void
548 Kumu::GenRandomUUID(byte_t* buf)
549 {
550   FortunaRNG RNG;
551   RNG.FillRandom(buf, UUID_Length);
552   buf[6] &= 0x0f; // clear bits 4-7
553   buf[6] |= 0x40; // set UUID version
554   buf[8] &= 0x3f; // clear bits 6&7
555   buf[8] |= 0x80; // set bit 7
556 }
557
558 //
559 void
560 Kumu::GenRandomValue(SymmetricKey& Key)
561 {
562   byte_t tmp_buf[SymmetricKey_Length];
563   FortunaRNG RNG;
564   RNG.FillRandom(tmp_buf, SymmetricKey_Length);
565   Key.Set(tmp_buf);
566 }
567
568
569 //------------------------------------------------------------------------------------------
570 // read a ber value from the buffer and compare with test value.
571 // Advances buffer to first character after BER value.
572 //
573 bool
574 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
575 {
576   if ( buf == 0 )
577     return false;
578
579   if ( ( **buf & 0x80 ) == 0 )
580     return false;
581
582   ui64_t val = 0;
583   ui8_t ber_size = ( **buf & 0x0f ) + 1;
584
585   if ( ber_size > 9 )
586     return false;
587
588   for ( ui8_t i = 1; i < ber_size; i++ )
589     {
590       if ( (*buf)[i] > 0 )
591         val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
592     }
593
594   *buf += ber_size;
595   return ( val == test_value );
596 }
597
598
599 //
600 bool
601 Kumu::read_BER(const byte_t* buf, ui64_t* val)
602 {
603   ui8_t ber_size, i;
604   
605   if ( buf == 0 || val == 0 )
606     return false;
607
608   if ( ( *buf & 0x80 ) == 0 )
609     return false;
610
611   *val = 0;
612   ber_size = ( *buf & 0x0f ) + 1;
613
614   if ( ber_size > 9 )
615     return false;
616
617   for ( i = 1; i < ber_size; i++ )
618     {
619       if ( buf[i] > 0 )
620         *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
621     }
622
623   return true;
624 }
625
626
627 static const ui64_t ber_masks[9] =
628   { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), 
629     ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
630     ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
631     ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
632     0
633   };
634
635 //
636 ui32_t
637 Kumu::get_BER_length_for_value(ui64_t val)
638 {
639   for ( ui32_t i = 0; i < 9; i++ )
640     {
641       if ( ( val & ber_masks[i] ) == 0 )
642         return i + 1;
643     }
644
645   ui64Printer tmp_i(val);
646   DefaultLogSink().Error("BER integer encoding not supported for large value %s\n", tmp_i.c_str());
647   return 0;
648 }
649
650 //
651 bool
652 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
653 {
654   if ( buf == 0 )
655     return false;
656
657   if ( ber_len == 0 )
658     { // calculate default length
659       if ( val < 0x01000000L )
660         ber_len = 4;
661       else if ( val < ui64_C(0x0100000000000000) )
662         ber_len = 8;
663       else
664         ber_len = 9;
665     }
666   else
667     { // sanity check BER length
668       if ( ber_len > 9 )
669         {
670           DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len);
671           return false;
672         }
673       
674       if ( ( val & ber_masks[ber_len - 1] ) != 0 )
675         {
676           ui64Printer tmp_i(val);
677           DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str());
678           return false;
679         }
680     }
681
682   buf[0] = 0x80 + ( ber_len - 1 );
683
684   for ( ui32_t i = ber_len - 1; i > 0; i-- )
685     {
686       buf[i] = (ui8_t)(val & 0xff);
687       val >>= 8;
688     }
689
690   return true;
691 }
692
693
694 //------------------------------------------------------------------------------------------
695
696 #ifndef KM_WIN32
697 #include <time.h>
698 #endif
699
700 //
701 Kumu::Timestamp::Timestamp() : m_TZOffsetMinutes(0) {
702   if (libdcp_test)
703     {
704       m_Timestamp.x = 42;
705     }
706   else
707     {
708       m_Timestamp.now();
709     }
710 }
711
712 Kumu::Timestamp::Timestamp(const Timestamp& rhs) {
713   m_Timestamp = rhs.m_Timestamp;
714   m_TZOffsetMinutes = rhs.m_TZOffsetMinutes;
715 }
716
717 Kumu::Timestamp::Timestamp(const char* datestr) : m_TZOffsetMinutes(0) {
718   DecodeString(datestr);
719 }
720
721 Kumu::Timestamp::~Timestamp() {
722 }
723
724 //
725 const Kumu::Timestamp&
726 Kumu::Timestamp::operator=(const Timestamp& rhs)
727 {
728   m_Timestamp = rhs.m_Timestamp;
729   m_TZOffsetMinutes = rhs.m_TZOffsetMinutes;
730   return *this;
731 }
732
733 bool Kumu::Timestamp::operator<(const Timestamp& rhs) const {
734   return m_Timestamp.x < rhs.m_Timestamp.x;
735 }
736
737 bool Kumu::Timestamp::operator>(const Timestamp& rhs) const {
738   return m_Timestamp.x > rhs.m_Timestamp.x;
739 }
740
741 bool Kumu::Timestamp::operator==(const Timestamp& rhs) const {
742   return m_Timestamp.x == rhs.m_Timestamp.x;
743 }
744
745 bool Kumu::Timestamp::operator!=(const Timestamp& rhs) const {
746   return m_Timestamp.x != rhs.m_Timestamp.x;
747 }
748
749 //
750 void
751 Kumu::Timestamp::GetComponents(ui16_t& Year, ui8_t&  Month, ui8_t&  Day,
752                                ui8_t&  Hour, ui8_t&  Minute, ui8_t&  Second) const
753 {
754   TAI::caltime ct;
755   ct = m_Timestamp;
756   Year = ct.date.year;
757   Month = ct.date.month;
758   Day = ct.date.day;
759   Hour = ct.hour;
760   Minute = ct.minute;
761   Second = ct.second;
762 }
763
764 //
765 void
766 Kumu::Timestamp::SetComponents(const ui16_t& Year, const ui8_t&  Month, const ui8_t&  Day,
767                                const ui8_t&  Hour, const ui8_t&  Minute, const ui8_t&  Second)
768 {
769   TAI::caltime ct;
770   ct.date.year = Year;
771   ct.date.month = Month;
772   ct.date.day = Day;
773   ct.hour = Hour;
774   ct.minute = Minute;
775   ct.second = Second;
776   ct.offset = 0;
777   m_Timestamp = ct;
778   m_TZOffsetMinutes = 0;
779 }
780
781 // returns false if the requested adjustment is out of range
782 bool
783 Kumu::Timestamp::SetTZOffsetMinutes(const i32_t& minutes)
784 {
785   static const i32_t tz_limit = 14 * 60 * 60;
786
787   if ( minutes < ( - tz_limit) || minutes > tz_limit )
788     return false;
789
790   m_TZOffsetMinutes = minutes;
791   return true;
792 }
793
794 // 
795 const char*
796 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
797 {
798   if ( buf_len < ( DateTimeLen + 1 ) )
799     return 0;
800
801   ui16_t year;
802   ui8_t month, day, hour, minute, second;
803   ui32_t ofst_hours = 0, ofst_minutes = 0;
804   char direction = '+';
805
806   if ( m_TZOffsetMinutes == 0 )
807     {
808       GetComponents(year, month, day, hour, minute, second);
809     }
810   else
811     {
812       // calculate local time
813       Kumu::Timestamp tmp_t(*this);
814       tmp_t.AddMinutes(m_TZOffsetMinutes);
815       tmp_t.GetComponents(year, month, day, hour, minute, second);
816
817       ofst_hours = abs(m_TZOffsetMinutes) / 60;
818       ofst_minutes = abs(m_TZOffsetMinutes) % 60;
819
820       if ( m_TZOffsetMinutes < 0 )
821         direction = '-';
822     }
823   
824   // 2004-05-01T13:20:00+00:00
825   snprintf(str_buf, buf_len,
826            "%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02hu:%02hu",
827            year, month, day, hour, minute, second,
828            direction, ofst_hours, ofst_minutes);
829
830   return str_buf;
831 }
832
833 //
834 bool
835 Kumu::Timestamp::DecodeString(const char* datestr)
836 {
837   if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
838        || datestr[4] != '-'
839        || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
840        || datestr[7] != '-'
841        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
842     return false;
843
844   ui32_t char_count = 10;
845   TAI::caltime YMDhms;
846   YMDhms.offset = 0;
847   YMDhms.date.year = atoi(datestr);
848   YMDhms.date.month = atoi(datestr + 5);
849   YMDhms.date.day = atoi(datestr + 8);
850  
851   if ( datestr[10] == 'T' )
852     {
853       if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
854            || datestr[13] != ':'
855            || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
856         return false;
857
858       char_count += 6;
859       YMDhms.hour = atoi(datestr + 11);
860       YMDhms.minute = atoi(datestr + 14);
861
862       if ( datestr[16] == ':' )
863         {
864           if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
865             return false;
866
867           char_count += 3;
868           YMDhms.second = atoi(datestr + 17);
869         }
870
871       if ( datestr[19] == '.' )
872         {
873           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
874             return false;
875           
876           // we don't carry the ms value
877           datestr += 4;
878         }
879
880       if ( datestr[19] == '-' || datestr[19] == '+' )
881         {
882           if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
883                || datestr[22] != ':'
884                || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
885             return false;
886
887           char_count += 6;
888
889           ui32_t TZ_hh = atoi(datestr + 20);
890           ui32_t TZ_mm = atoi(datestr + 23);
891           if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
892             return false;
893
894           i32_t TZ_offset = 60 * TZ_hh + TZ_mm;
895           if (datestr[19] == '-')
896             TZ_offset = -TZ_offset;
897           /* at this point, TZ_offset reflects the contents of the string */
898
899           /* a negative offset is behind UTC and so needs to increment to
900            * convert, while a positive offset must do the reverse */
901           YMDhms.offset = TZ_offset;
902         }
903       else if (datestr[19] == 'Z')
904         {
905           /* act as if the offset were +00:00 */
906           char_count++;
907         }
908     }
909
910   if ( datestr[char_count] != 0 )
911     {
912       Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
913                                    datestr, char_count);
914       return false;
915     }
916   
917   m_Timestamp = YMDhms;
918   m_TZOffsetMinutes = YMDhms.offset;
919   return true;
920 }
921
922 //
923 bool
924 Kumu::Timestamp::HasValue() const
925 {
926   return true;
927 }
928
929 //
930 bool
931 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
932 {
933   ui16_t year;
934   ui8_t month, day, hour, minute, second, tick;
935
936   assert(Reader);
937   if ( ! Reader->ReadUi16BE(&year) ) return false;
938   if ( ! Reader->ReadUi8(&month) ) return false;
939   if ( ! Reader->ReadUi8(&day) ) return false;
940   if ( ! Reader->ReadUi8(&hour) ) return false;
941   if ( ! Reader->ReadUi8(&minute) ) return false;
942   if ( ! Reader->ReadUi8(&second) ) return false;
943   if ( ! Reader->ReadUi8(&tick) ) return false;
944   SetComponents(year, month, day, hour, minute, second);
945   return true;
946 }
947
948 //
949 bool
950 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
951 {
952   assert(Writer);
953
954   ui16_t year;
955   ui8_t month, day, hour, minute, second, tick = 0;
956   GetComponents(year, month, day, hour, minute, second);
957
958   if ( ! Writer->WriteUi16BE(year) ) return false;      
959   if ( ! Writer->WriteUi8(month) ) return false;
960   if ( ! Writer->WriteUi8(day) ) return false;
961   if ( ! Writer->WriteUi8(hour) ) return false;
962   if ( ! Writer->WriteUi8(minute) ) return false;
963   if ( ! Writer->WriteUi8(second) ) return false;
964   if ( ! Writer->WriteUi8(tick) ) return false;
965   return true;
966 }
967
968 //
969 ui64_t
970 Kumu::Timestamp::GetCTime() const
971 {
972   return m_Timestamp.x - ui64_C(4611686018427387914);
973 }
974
975
976 //------------------------------------------------------------------------------------------
977
978 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
979   : m_p(0), m_capacity(0), m_size(0)
980 {
981   m_p = Buf->Data();
982   m_capacity = Buf->Capacity();
983   assert(m_p); assert(m_capacity);
984 }
985
986 bool
987 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
988 {
989   if ( ( m_size + ber_len ) > m_capacity )
990     return false;
991
992   if ( ! write_BER(m_p + m_size, i, ber_len) )
993     return false;
994
995   m_size += ber_len;
996   return true;
997 }
998
999
1000 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1001   : m_p(0), m_capacity(0), m_size(0)
1002 {
1003   m_p = Buf->RoData();
1004   m_capacity = Buf->Length();
1005   assert(m_p); assert(m_capacity);
1006 }
1007
1008 bool
1009 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1010 {
1011   if ( i == 0 || ber_len == 0 ) return false;
1012
1013   if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1014     return false;
1015
1016   if ( ( m_size + *ber_len ) > m_capacity )
1017     return false;
1018
1019   if ( ! read_BER(m_p + m_size, i) )
1020     return false;
1021
1022   m_size += *ber_len;
1023   return true;
1024 }
1025
1026 //------------------------------------------------------------------------------------------
1027
1028 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1029
1030 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1031 {
1032   Capacity(cap);
1033 }
1034
1035 Kumu::ByteString::~ByteString()
1036 {
1037   if ( m_Data != 0 )
1038     free(m_Data);
1039 }
1040
1041
1042 // copy the given data into the ByteString, set Length value.
1043 // Returns error if the ByteString is too small.
1044 Kumu::Result_t
1045 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1046 {
1047   if ( m_Capacity < buf_len )
1048     return RESULT_ALLOC;
1049
1050   memcpy(m_Data, buf, buf_len);
1051   m_Length = buf_len;
1052   return RESULT_OK;
1053 }
1054
1055
1056 // copy the given data into the ByteString, set Length value.
1057 // Returns error if the ByteString is too small.
1058 Kumu::Result_t
1059 Kumu::ByteString::Set(const ByteString& Buf)
1060 {
1061   if ( m_Capacity < Buf.m_Capacity )
1062     return RESULT_ALLOC;
1063
1064   memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1065   m_Length = Buf.m_Length;
1066   return RESULT_OK;
1067 }
1068
1069
1070 // Sets the size of the internally allocate buffer.
1071 Kumu::Result_t
1072 Kumu::ByteString::Capacity(ui32_t cap_size)
1073 {
1074   if ( m_Capacity >= cap_size )
1075     return RESULT_OK;
1076
1077   byte_t* tmp_data = 0;
1078   if ( m_Data != 0 )
1079     {
1080       if ( m_Length > 0 )
1081         tmp_data = m_Data;
1082       else
1083         free(m_Data);
1084     }
1085                 
1086   if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1087     return RESULT_ALLOC;
1088
1089   if ( tmp_data != 0 )
1090     {
1091       assert(m_Length > 0);
1092       memcpy(m_Data, tmp_data, m_Length);
1093       free(tmp_data);
1094     }
1095                 
1096   m_Capacity = cap_size;
1097   return RESULT_OK;
1098 }
1099
1100 //
1101 Kumu::Result_t
1102 Kumu::ByteString::Append(const ByteString& Buf)
1103 {
1104   Result_t result = RESULT_OK;
1105   ui32_t diff = m_Capacity - m_Length;
1106
1107   if ( diff < Buf.Length() )
1108     result = Capacity(m_Capacity + Buf.Length());
1109
1110   if ( KM_SUCCESS(result) )
1111     {
1112       memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1113       m_Length += Buf.Length();
1114     }
1115
1116   return result;
1117 }
1118
1119 //
1120 Kumu::Result_t
1121 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1122 {
1123   Result_t result = RESULT_OK;
1124   ui32_t diff = m_Capacity - m_Length;
1125
1126   if ( diff < buf_len )
1127     result = Capacity(m_Capacity + buf_len);
1128
1129   if ( KM_SUCCESS(result) )
1130     {
1131       memcpy(m_Data + m_Length, buf, buf_len);
1132       m_Length += buf_len;
1133     }
1134
1135   return result;
1136 }
1137
1138
1139 //
1140 // end KM_util.cpp
1141 //