added version info to libpyasdcp (same as other lib versions)
[asdcplib.git] / src / KM_util.cpp
index 1d2cc114031db0969ae264bb133b4bf2bff748f7..2624f07fcb1bf6533153c787c9892360b07e9564 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005-2006, John Hurst
+Copyright (c) 2005-2009, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -34,29 +34,37 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <KM_memio.h>
 #include <KM_fileio.h>
 #include <KM_log.h>
+#include <KM_tai.h>
 #include <ctype.h>
 #include <list>
 #include <map>
 #include <string>
 
+const char*
+Kumu::Version()
+{
+  return PACKAGE_VERSION;
+}
+
+
 //------------------------------------------------------------------------------------------
 
 // Result_t Internals
 
 struct map_entry_t
 {
-  long            rcode;
+  int             rcode;
   Kumu::Result_t* result;
 };
 
-const ui32_t MapMax = 512;
+const ui32_t MapMax = 1024;
 const ui32_t MapSize = MapMax * (sizeof(struct map_entry_t));
 static bool s_MapInit = false;
 static struct map_entry_t s_ResultMap[MapSize];
 
 //
 const Kumu::Result_t&
-Kumu::Result_t::Find(long v)
+Kumu::Result_t::Find(int v)
 {
   if ( v == 0 )
     return RESULT_OK;
@@ -67,14 +75,42 @@ Kumu::Result_t::Find(long v)
        return *s_ResultMap[i].result;
     }
 
-  DefaultLogSink().Error("Unknown result code: %ld\n", v);
-  return RESULT_FAIL;
+  return RESULT_UNKNOWN;
+}
+
+//
+Kumu::Result_t
+Kumu::Result_t::Delete(int v)
+{
+  if ( v >= RESULT_NOTAFILE.Value() )
+    {
+      DefaultLogSink().Error("Cannot delete core result code: %ld\n", v);
+      return RESULT_FAIL;
+    }
+
+  for ( ui32_t i = 0; s_ResultMap[i].result != 0 && i < MapMax; i++ )
+    {
+      if ( s_ResultMap[i].rcode == v )
+       {
+         s_ResultMap[i].rcode = 0;
+         s_ResultMap[i++].result = 0;
+
+         for ( ; s_ResultMap[i].result != 0 && i < MapMax; i++ )
+           s_ResultMap[i-1] = s_ResultMap[i];
+
+         return RESULT_OK;
+       }
+    }
+
+  return RESULT_FALSE;
 }
 
+
 //
-Kumu::Result_t::Result_t(long v, const char* l) : value(v), label(l)
+Kumu::Result_t::Result_t(int v, const char* l) : value(v), label(l)
 {
   assert(l);
+  assert(value < (int)MapMax);
 
   if ( v == 0 )
     return;
@@ -110,6 +146,26 @@ Kumu::Result_t::Result_t(long v, const char* l) : value(v), label(l)
 Kumu::Result_t::~Result_t() {}
 
 
+//------------------------------------------------------------------------------------------
+// DTrace internals
+
+static int s_DTraceSequence = 0;
+
+Kumu::DTrace_t::DTrace_t(const char* Label, Kumu::Result_t* Watch, int Line, const char* File)
+  : m_Label(Label), m_Watch(Watch), m_Line(Line), m_File(File)
+{
+  m_Sequence = s_DTraceSequence++;
+  DefaultLogSink().Debug("@enter %s[%d] (%s at %d)\n", m_Label, m_Sequence, m_File, m_Line);
+}
+
+Kumu::DTrace_t::~DTrace_t()
+{
+  if ( m_Watch != 0  )
+    DefaultLogSink().Debug("@exit %s[%d]: %s\n", m_Label, m_Sequence, m_Watch->Label());
+  else
+    DefaultLogSink().Debug("@exit %s[%d]\n", m_Label, m_Sequence);
+}
+
 //------------------------------------------------------------------------------------------
 
 
@@ -316,6 +372,38 @@ Kumu::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size)
   return 0;
 }
 
+#ifdef CONFIG_RANDOM_UUID
+
+// convert a memory region to a NULL-terminated hexadecimal string
+//
+static const char*
+bin2hex_rand(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
+{
+  if ( bin_buf == 0
+       || str_buf == 0
+       || ((bin_len * 2) + 1) > str_len )
+    return 0;
+
+  char* p = str_buf;
+  Kumu::mem_ptr<byte_t> rand_buf = new byte_t[bin_len];
+  Kumu::FortunaRNG RNG;
+  RNG.FillRandom(rand_buf, bin_len);
+
+  for ( ui32_t i = 0; i < bin_len; i++ )
+    {
+      *p = (bin_buf[i] >> 4) & 0x0f;
+      *p += *p < 10 ? 0x30 : (( ((rand_buf[i] & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
+      p++;
+
+      *p = bin_buf[i] & 0x0f;
+      *p += *p < 10 ? 0x30 : (( (((rand_buf[i] >> 1) & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
+      p++;
+    }
+
+  *p = '\0';
+  return str_buf;
+}
+#endif
 
 // convert a memory region to a NULL-terminated hexadecimal string
 //
@@ -327,6 +415,12 @@ Kumu::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_l
        || ((bin_len * 2) + 1) > str_len )
     return 0;
 
+#ifdef CONFIG_RANDOM_UUID
+  const char* use_random_uuid  = getenv("KM_USE_RANDOM_UUID");
+  if ( use_random_uuid != 0 && use_random_uuid[0] != 0 && use_random_uuid[0] != '0' )
+    return bin2hex_rand(bin_buf, bin_len, str_buf, str_len);
+#endif
+
   char* p = str_buf;
 
   for ( ui32_t i = 0; i < bin_len; i++ )
@@ -443,9 +537,6 @@ Kumu::GenRandomValue(SymmetricKey& Key)
 
 
 //------------------------------------------------------------------------------------------
-// read a ber value from the buffer and compare with test value.
-// Advances buffer to first character after BER value.
-
 // read a ber value from the buffer and compare with test value.
 // Advances buffer to first character after BER value.
 //
@@ -511,6 +602,20 @@ static const ui64_t ber_masks[9] =
     0
   };
 
+//
+ui32_t
+Kumu::get_BER_length_for_value(ui64_t val)
+{
+  for ( ui32_t i = 0; i < 9; i++ )
+    {
+      if ( ( val & ber_masks[i] ) == 0 )
+       return i + 1;
+    }
+
+  ui64Printer tmp_i(val);
+  DefaultLogSink().Error("BER integer encoding not supported for large value %s\n", tmp_i.c_str());
+  return 0;
+}
 
 //
 bool
@@ -532,14 +637,14 @@ Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
     { // sanity check BER length
       if ( ber_len > 9 )
         {
-          DefaultLogSink().Error("BER size %u exceeds maximum size of 9\n", ber_len);
+          DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len);
           return false;
         }
       
-      if ( val & ber_masks[ber_len - 1] )
+      if ( ( val & ber_masks[ber_len - 1] ) != 0 )
         {
          ui64Printer tmp_i(val);
-          DefaultLogSink().Error("BER size %u too small for value %s\n", tmp_i.c_str());
+          DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str());
           return false;
         }
     }
@@ -633,7 +738,7 @@ Kumu::Timestamp::AddDays(i32_t days)
       TIMESTAMP_TO_SYSTIME(*this, &current_st);
       SystemTimeToFileTime(&current_st, &current_ft);
       memcpy(&current_ul, &current_ft, sizeof(current_ul));
-      current_ul.QuadPart += ( seconds_to_ns100(86400) * (ui64_t)days );
+      current_ul.QuadPart += ( seconds_to_ns100(86400) * (i64_t)days );
       memcpy(&current_ft, &current_ul, sizeof(current_ft));
       FileTimeToSystemTime(&current_ft, &current_st);
       SYSTIME_TO_TIMESTAMP(&current_st, *this);
@@ -653,7 +758,47 @@ Kumu::Timestamp::AddHours(i32_t hours)
       TIMESTAMP_TO_SYSTIME(*this, &current_st);
       SystemTimeToFileTime(&current_st, &current_ft);
       memcpy(&current_ul, &current_ft, sizeof(current_ul));
-      current_ul.QuadPart += ( seconds_to_ns100(3600) * (ui64_t)hours );
+      current_ul.QuadPart += ( seconds_to_ns100(3600) * (i64_t)hours );
+      memcpy(&current_ft, &current_ul, sizeof(current_ft));
+      FileTimeToSystemTime(&current_ft, &current_st);
+      SYSTIME_TO_TIMESTAMP(&current_st, *this);
+    }
+}
+
+//
+void
+Kumu::Timestamp::AddMinutes(i32_t minutes)
+{
+  SYSTEMTIME current_st;
+  FILETIME current_ft;
+  ULARGE_INTEGER current_ul;
+
+  if ( minutes != 0 )
+    {
+      TIMESTAMP_TO_SYSTIME(*this, &current_st);
+      SystemTimeToFileTime(&current_st, &current_ft);
+      memcpy(&current_ul, &current_ft, sizeof(current_ul));
+      current_ul.QuadPart += ( seconds_to_ns100(60) * (i64_t)minutes );
+      memcpy(&current_ft, &current_ul, sizeof(current_ft));
+      FileTimeToSystemTime(&current_ft, &current_st);
+      SYSTIME_TO_TIMESTAMP(&current_st, *this);
+    }
+}
+
+//
+void
+Kumu::Timestamp::AddSeconds(i32_t seconds)
+{
+  SYSTEMTIME current_st;
+  FILETIME current_ft;
+  ULARGE_INTEGER current_ul;
+
+  if ( seconds != 0 )
+    {
+      TIMESTAMP_TO_SYSTIME(*this, &current_st);
+      SystemTimeToFileTime(&current_st, &current_ft);
+      memcpy(&current_ul, &current_ft, sizeof(current_ul));
+      current_ul.QuadPart += ( seconds_to_ns100(1) * (i64_t)seconds );
       memcpy(&current_ft, &current_ul, sizeof(current_ft));
       FileTimeToSystemTime(&current_ft, &current_st);
       SYSTIME_TO_TIMESTAMP(&current_st, *this);
@@ -664,64 +809,80 @@ Kumu::Timestamp::AddHours(i32_t hours)
 
 #include <time.h>
 
-#define TIMESTAMP_TO_TM(ts, t) \
-  (t)->tm_year = (ts).Year - 1900;   /* year - 1900 */ \
-  (t)->tm_mon  = (ts).Month - 1;     /* month of year (0 - 11) */ \
-  (t)->tm_mday = (ts).Day;           /* day of month (1 - 31) */ \
-  (t)->tm_hour = (ts).Hour;          /* hours (0 - 23) */ \
-  (t)->tm_min  = (ts).Minute;        /* minutes (0 - 59) */ \
-  (t)->tm_sec  = (ts).Second;        /* seconds (0 - 60) */
-
-#define TM_TO_TIMESTAMP(t, ts) \
-  (ts).Year   = (t)->tm_year + 1900;    /* year - 1900 */ \
-  (ts).Month  = (t)->tm_mon + 1;     /* month of year (0 - 11) */ \
-  (ts).Day    = (t)->tm_mday;    /* day of month (1 - 31) */ \
-  (ts).Hour   = (t)->tm_hour;    /* hours (0 - 23) */ \
-  (ts).Minute = (t)->tm_min;     /* minutes (0 - 59) */ \
-  (ts).Second = (t)->tm_sec;     /* seconds (0 - 60) */
+#define TIMESTAMP_TO_CALTIME(ts, ct)                                  \
+  (ct)->date.year  = (ts).Year;    /* year */                         \
+  (ct)->date.month = (ts).Month;   /* month of year (1 - 12) */               \
+  (ct)->date.day   = (ts).Day;     /* day of month (1 - 31) */        \
+  (ct)->hour       = (ts).Hour;    /* hours (0 - 23) */                       \
+  (ct)->minute     = (ts).Minute;  /* minutes (0 - 59) */             \
+  (ct)->second     = (ts).Second;  /* seconds (0 - 60) */             \
+  (ct)->offset     = 0;
+
+#define CALTIME_TO_TIMESTAMP(ct, ts)                                  \
+  assert((ct)->offset == 0);                                          \
+  (ts).Year   = (ct)->date.year;    /* year */                        \
+  (ts).Month  = (ct)->date.month;   /* month of year (1 - 12) */       \
+  (ts).Day    = (ct)->date.day;     /* day of month (1 - 31) */               \
+  (ts).Hour   = (ct)->hour;         /* hours (0 - 23) */              \
+  (ts).Minute = (ct)->minute;       /* minutes (0 - 59) */            \
+  (ts).Second = (ct)->second;       /* seconds (0 - 60) */
+
 
 //
 Kumu::Timestamp::Timestamp() :
-  Year(0), Month(0),  Day(0), Hour(0), Minute(0), Second(0)
+  Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
 {
-  time_t t_now = time(0);
-  struct tm*  now = gmtime(&t_now);
-  TM_TO_TIMESTAMP(now, *this);
+  Kumu::TAI::tai now;
+  Kumu::TAI::caltime ct;
+  now.now();
+  ct = now;
+  CALTIME_TO_TIMESTAMP(&ct, *this)
 }
 
 //
 bool
 Kumu::Timestamp::operator<(const Timestamp& rhs) const
 {
-  struct tm lhtm, rhtm;
-  TIMESTAMP_TO_TM(*this, &lhtm);
-  TIMESTAMP_TO_TM(rhs, &rhtm);
-  return ( timegm(&lhtm) < timegm(&rhtm) );
+  Kumu::TAI::caltime lh_ct, rh_ct;
+  TIMESTAMP_TO_CALTIME(*this, &lh_ct)
+  TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
+
+  Kumu::TAI::tai lh_tai, rh_tai;
+  lh_tai = lh_ct;
+  rh_tai = rh_ct;
+
+  return ( lh_tai.x < rh_tai.x );
 }
 
 //
 bool
 Kumu::Timestamp::operator>(const Timestamp& rhs) const
 {
-  struct tm lhtm, rhtm;
-  TIMESTAMP_TO_TM(*this, &lhtm);
-  TIMESTAMP_TO_TM(rhs, &rhtm);
-  return ( timegm(&lhtm) > timegm(&rhtm) );
+  Kumu::TAI::caltime lh_ct, rh_ct;
+  TIMESTAMP_TO_CALTIME(*this, &lh_ct)
+  TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
+
+  Kumu::TAI::tai lh_tai, rh_tai;
+  lh_tai = lh_ct;
+  rh_tai = rh_ct;
+
+  return ( lh_tai.x > rh_tai.x );
 }
 
 //
 void
 Kumu::Timestamp::AddDays(i32_t days)
 {
-  struct tm current;
+  Kumu::TAI::caltime ct;
+  Kumu::TAI::tai t;
 
   if ( days != 0 )
     {
-      TIMESTAMP_TO_TM(*this, &current);
-      time_t adj_time = timegm(&current);
-      adj_time += 86400 * days;
-      struct tm*  now = gmtime(&adj_time);
-      TM_TO_TIMESTAMP(now, *this);
+      TIMESTAMP_TO_CALTIME(*this, &ct)
+      t = ct;
+      t.add_days(days);
+      ct = t;
+      CALTIME_TO_TIMESTAMP(&ct, *this)
     }
 }
 
@@ -729,22 +890,57 @@ Kumu::Timestamp::AddDays(i32_t days)
 void
 Kumu::Timestamp::AddHours(i32_t hours)
 {
-  struct tm current;
+  Kumu::TAI::caltime ct;
+  Kumu::TAI::tai t;
 
   if ( hours != 0 )
     {
-      TIMESTAMP_TO_TM(*this, &current);
-      time_t adj_time = timegm(&current);
-      adj_time += 3600 * hours;
-      struct tm*  now = gmtime(&adj_time);
-      TM_TO_TIMESTAMP(now, *this);
+      TIMESTAMP_TO_CALTIME(*this, &ct)
+      t = ct;
+      t.add_hours(hours);
+      ct = t;
+      CALTIME_TO_TIMESTAMP(&ct, *this)
+    }
+}
+
+//
+void
+Kumu::Timestamp::AddMinutes(i32_t minutes)
+{
+  Kumu::TAI::caltime ct;
+  Kumu::TAI::tai t;
+
+  if ( minutes != 0 )
+    {
+      TIMESTAMP_TO_CALTIME(*this, &ct)
+      t = ct;
+      t.add_minutes(minutes);
+      ct = t;
+      CALTIME_TO_TIMESTAMP(&ct, *this)
+    }
+}
+
+//
+void
+Kumu::Timestamp::AddSeconds(i32_t seconds)
+{
+  Kumu::TAI::caltime ct;
+  Kumu::TAI::tai t;
+
+  if ( seconds != 0 )
+    {
+      TIMESTAMP_TO_CALTIME(*this, &ct)
+      t = ct;
+      t.add_seconds(seconds);
+      ct = t;
+      CALTIME_TO_TIMESTAMP(&ct, *this)
     }
 }
 
 #endif // KM_WIN32
 
 
-Kumu::Timestamp::Timestamp(const Timestamp& rhs)
+Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
 {
   Year   = rhs.Year;
   Month  = rhs.Month;
@@ -804,15 +1000,42 @@ Kumu::Timestamp::operator!=(const Timestamp& rhs) const
 // 
 const char*
 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
+{
+  return EncodeStringWithOffset(str_buf, buf_len, 0);
+}
+
+// 
+const char*
+Kumu::Timestamp::EncodeStringWithOffset(char* str_buf, ui32_t buf_len,
+                                       i32_t offset_minutes) const
 {
   if ( buf_len < ( DateTimeLen + 1 ) )
     return 0;
 
-  // 2004-05-01T13:20:00-00:00
+  // ensure offset is within +/- 14 hours
+  if ((offset_minutes < -14 * 60) || (offset_minutes > 14 * 60))
+    return 0;
+
+  // set the apparent time
+  Kumu::Timestamp tmp_t(*this);
+  tmp_t.AddMinutes(offset_minutes);
+
+  char direction = '+';
+  if (offset_minutes < 0) {
+    direction = '-';
+    // need absolute offset from zero
+    offset_minutes = -offset_minutes;
+  }
+
+  // 2004-05-01T13:20:00+00:00
   snprintf(str_buf, buf_len,
-          "%04hu-%02hu-%02huT%02hu:%02hu:%02hu-00:00",
-          Year, Month, Day, Hour, Minute, Second);
-  
+          "%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02hu:%02hu",
+          tmp_t.Year, tmp_t.Month, tmp_t.Day,
+          tmp_t.Hour, tmp_t.Minute, tmp_t.Second,
+          direction,
+          offset_minutes / 60,
+          offset_minutes % 60);
+
   return str_buf;
 }
 
@@ -829,6 +1052,7 @@ Kumu::Timestamp::DecodeString(const char* datestr)
        || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
     return false;
 
+  ui32_t char_count = 10;
   TmpStamp.Year = atoi(datestr);
   TmpStamp.Month = atoi(datestr + 5);
   TmpStamp.Day = atoi(datestr + 8);
@@ -841,6 +1065,7 @@ Kumu::Timestamp::DecodeString(const char* datestr)
           || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
        return false;
 
+      char_count += 6;
       TmpStamp.Hour = atoi(datestr + 11);
       TmpStamp.Minute = atoi(datestr + 14);
 
@@ -849,28 +1074,54 @@ Kumu::Timestamp::DecodeString(const char* datestr)
          if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
            return false;
 
+         char_count += 3;
          TmpStamp.Second = atoi(datestr + 17);
        }
-    }
 
-  if ( datestr[19] == '-' || datestr[19] == '+' )
-    {
-      if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
-          || datestr[22] != ':'
-          || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
-       return false;
+      if ( datestr[19] == '.' )
+       {
+         if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
+           return false;
+         
+         // we don't carry the ms value
+         datestr += 4;
+       }
 
-      ui32_t TZ_hh = atoi(datestr + 20);
-      ui32_t TZ_mm = atoi(datestr + 23);
-      
-      if ( TZ_mm != 0 )
-       DefaultLogSink().Error("Ignoring minutes in timezone offset: %u\n", TZ_mm);
+      if ( datestr[19] == '-' || datestr[19] == '+' )
+       {
+         if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
+              || datestr[22] != ':'
+              || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
+           return false;
 
-      if ( TZ_hh > 12 )
-       return false;
+         char_count += 6;
+
+         ui32_t TZ_hh = atoi(datestr + 20);
+         ui32_t TZ_mm = atoi(datestr + 23);
+         if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
+           return false;
+
+         i32_t TZ_offset = 60 * TZ_hh + TZ_mm;
+         if (datestr[19] == '-')
+           TZ_offset = -TZ_offset;
+         /* at this point, TZ_offset reflects the contents of the string */
+
+         /* a negative offset is behind UTC and so needs to increment to
+          * convert, while a positive offset must do the reverse */
+         TmpStamp.AddMinutes(-TZ_offset);
+       }
+      else if (datestr[19] == 'Z')
+       {
+         /* act as if the offset were +00:00 */
+         char_count++;
+       }
+    }
 
-      else 
-       AddHours( (datestr[19] == '-' ? (-TZ_hh) : TZ_hh));
+  if ( datestr[char_count] != 0 )
+    {
+      Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
+                                  datestr, char_count);
+      return false;
     }
 
 #ifdef KM_WIN32
@@ -881,11 +1132,12 @@ Kumu::Timestamp::DecodeString(const char* datestr)
     return false;
   SYSTIME_TO_TIMESTAMP(&st, *this);
 #else
-  struct tm stm;
-  TIMESTAMP_TO_TM(TmpStamp, &stm);
-  if ( timegm(&stm) == 0 )
-    return false;
-  TM_TO_TIMESTAMP(&stm, *this);
+  Kumu::TAI::tai t;
+  Kumu::TAI::caltime ct;
+  TIMESTAMP_TO_CALTIME(TmpStamp, &ct);
+  t = ct; // back and forth to tai to normalize offset
+  ct = t;
+  CALTIME_TO_TIMESTAMP(&ct, *this)
 #endif
 
   return true;
@@ -921,30 +1173,44 @@ Kumu::Timestamp::Archive(MemIOWriter* Writer) const
   return true;
 }
 
-#if 0
 //
-bool
-Kumu::UnarchiveString(MemIOReader* Reader, std::string&)
-{
-  assert(Reader);
-  ui32_t str_length;
-  if ( ! Reader->ReadUi32BE(&str_length) ) return false;
-  assign((const char*)Reader->CurrentData(), str_length);
-  if ( ! Reader->SkipOffset(str_length) ) return false;
-  return true;
-}
-
-//
-bool
-Kumu::String::Archive(MemIOWriter* Writer) const
+long
+Kumu::Timestamp::GetSecondsSinceEpoch(void) const
 {
-  assert(Writer);
-  if ( ! Writer->WriteUi32BE(length()) ) return false;
-  if ( ! Writer->WriteRaw((const byte_t*)c_str(), length()) ) return false;
+#ifdef KM_WIN32
+  SYSTEMTIME timeST;
+  TIMESTAMP_TO_SYSTIME(*this, &timeST);
+  FILETIME timeFT;
+  SystemTimeToFileTime(&timeST, &timeFT);
+  ULARGE_INTEGER timeUL;
+  timeUL.LowPart = timeFT.dwLowDateTime;
+  timeUL.HighPart = timeFT.dwHighDateTime;
+
+  SYSTEMTIME epochST;
+  epochST.wYear = 1970;
+  epochST.wMonth = 0;
+  epochST.wDayOfWeek = 4;
+  epochST.wDay = 1;
+  epochST.wHour = 0;
+  epochST.wMinute = 0;
+  epochST.wSecond = 0;
+  epochST.wMilliseconds = 0;
+  FILETIME epochFT;
+  SystemTimeToFileTime(&epochST, &epochFT);
+  ULARGE_INTEGER epochUL;
+  epochUL.LowPart = epochFT.dwLowDateTime;
+  epochUL.HighPart = epochFT.dwHighDateTime;
+
+  return (timeUL.QuadPart - epochUL.QuadPart) / 10000000;
+#else
+  Kumu::TAI::caltime ct;
+  Kumu::TAI::tai t;
+  TIMESTAMP_TO_CALTIME(*this, &ct);
+  t = ct;
 
-  return true;
-}
+  return (long) (t.x - ui64_C(4611686018427387914));
 #endif
+}
 
 //------------------------------------------------------------------------------------------
 
@@ -1026,25 +1292,47 @@ Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
 }
 
 
+// copy the given data into the ByteString, set Length value.
+// Returns error if the ByteString is too small.
+Kumu::Result_t
+Kumu::ByteString::Set(const ByteString& Buf)
+{
+  if ( m_Capacity < Buf.m_Capacity )
+    return RESULT_ALLOC;
+
+  memcpy(m_Data, Buf.m_Data, Buf.m_Length);
+  m_Length = Buf.m_Length;
+  return RESULT_OK;
+}
+
+
 // Sets the size of the internally allocate buffer.
-// Resets content length to zero.
 Kumu::Result_t
 Kumu::ByteString::Capacity(ui32_t cap_size)
 {
-  if ( m_Capacity < cap_size )
+  if ( m_Capacity >= cap_size )
+    return RESULT_OK;
+
+  byte_t* tmp_data = 0;
+  if ( m_Data != 0 )
     {
-      if ( m_Data != 0 )
+      if ( m_Length > 0 )
+       tmp_data = m_Data;
+      else
        free(m_Data);
+    }
                
-      m_Data = (byte_t*)malloc(cap_size);
-               
-      if ( m_Data == 0 )
-       return RESULT_ALLOC;
-               
-      m_Capacity = cap_size;
-      m_Length = 0;
+  if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
+    return RESULT_ALLOC;
+
+  if ( tmp_data != 0 )
+    {
+      assert(m_Length > 0);
+      memcpy(m_Data, tmp_data, m_Length);
+      free(tmp_data);
     }
-       
+               
+  m_Capacity = cap_size;
   return RESULT_OK;
 }