2 Copyright (c) 2005-2011, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
29 \brief Utility functions
35 #include <KM_fileio.h>
47 return PACKAGE_VERSION;
51 //------------------------------------------------------------------------------------------
58 Kumu::Result_t* result;
62 // WIN32 does not init this in time for use with Result_t(...) below, so it is
63 // now a pointer that Result_t(...) fills in when it needs it.
64 static Kumu::Mutex* s_MapLock = 0;
66 static ui32_t s_MapSize = 0;
67 static const ui32_t MapMax = 2048;
68 static struct map_entry_t s_ResultMap[MapMax];
73 Kumu::Result_t::Find(int v)
79 AutoMutex L(*s_MapLock);
81 for ( ui32_t i = 0; i < s_MapSize; ++i )
83 if ( s_ResultMap[i].rcode == v )
84 return *s_ResultMap[i].result;
87 return RESULT_UNKNOWN;
92 Kumu::Result_t::Delete(int v)
94 if ( v < -99 || v > 99 )
96 DefaultLogSink().Error("Cannot delete core result code: %ld\n", v);
101 AutoMutex L(*s_MapLock);
103 for ( ui32_t i = 0; i < s_MapSize; ++i )
105 if ( s_ResultMap[i].rcode == v )
107 for ( ++i; i < s_MapSize; ++i )
108 s_ResultMap[i-1] = s_ResultMap[i];
120 Kumu::Result_t::End()
126 const Kumu::Result_t&
127 Kumu::Result_t::Get(unsigned int i)
129 return *s_ResultMap[i].result;
133 Kumu::Result_t::Result_t(int v, const char* s, const char* l) : value(v), symbol(s), label(l)
141 // This may seem tricky, but it is certain that the static values declared in KM_error.h will
142 // be created (and thus this method will be called) before main(...) is called. It is not
143 // until then that threads could be created, thus the mutex will exist before multi-threaded
144 // access could occur.
145 if ( s_MapLock == 0 )
146 s_MapLock = new Kumu::Mutex;
149 AutoMutex L(*s_MapLock);
151 for ( ui32_t i = 0; i < s_MapSize; ++i )
153 if ( s_ResultMap[i].rcode == v )
157 assert(s_MapSize+1 < MapMax);
159 s_ResultMap[s_MapSize].rcode = v;
160 s_ResultMap[s_MapSize].result = this;
166 Kumu::Result_t::~Result_t() {}
169 //------------------------------------------------------------------------------------------
172 static int s_DTraceSequence = 0;
174 Kumu::DTrace_t::DTrace_t(const char* Label, Kumu::Result_t* Watch, int Line, const char* File)
175 : m_Label(Label), m_Watch(Watch), m_Line(Line), m_File(File)
177 m_Sequence = s_DTraceSequence++;
178 DefaultLogSink().Debug("@enter %s[%d] (%s at %d)\n", m_Label, m_Sequence, m_File, m_Line);
181 Kumu::DTrace_t::~DTrace_t()
184 DefaultLogSink().Debug("@exit %s[%d]: %s\n", m_Label, m_Sequence, m_Watch->Label());
186 DefaultLogSink().Debug("@exit %s[%d]\n", m_Label, m_Sequence);
189 //------------------------------------------------------------------------------------------
192 const char fill = '=';
193 const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
195 const byte_t decode_map[] =
196 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
197 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
198 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
199 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
200 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
201 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63,
202 52, 53, 54, 55, 56, 57, 58, 59,
203 60, 61, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
204 0xff, 0, 1, 2, 3, 4, 5, 6,
205 7, 8, 9, 10, 11, 12, 13, 14,
206 15, 16, 17, 18, 19, 20, 21, 22,
207 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff,
208 0xff, 26, 27, 28, 29, 30, 31, 32,
209 33, 34, 35, 36, 37, 38, 39, 40,
210 41, 42, 43, 44, 45, 46, 47, 48,
211 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff,
212 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
213 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
214 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
215 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
216 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
217 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
218 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
219 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
220 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
221 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
222 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
223 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
224 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
225 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
226 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
227 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
231 // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer
232 // if the binary buffer was large enough to hold the result. If the output buffer
233 // is too small or any of the pointer arguments are NULL, the subroutine will
237 Kumu::base64encode(const byte_t* buf, ui32_t buf_len, char* strbuf, ui32_t strbuf_len)
240 ui32_t i, block_len, diff;
242 if ( buf == 0 || strbuf == 0 )
245 if ( strbuf_len < base64_encode_length(buf_len) + 1 )
250 while ( block_len % 3 )
253 for ( i = 0; i < block_len; i += 3 )
255 strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
256 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
257 strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) | ( buf[2] >> 6 ) )];
258 strbuf[out_char++] = base64_chars[( buf[2] & 0x3f )];
268 strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
272 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) )];
273 strbuf[out_char++] = fill;
275 else if ( diff == 2 )
277 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
278 strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) )];
281 strbuf[out_char++] = fill;
284 strbuf[out_char] = 0;
291 // Convert NULL-terminated UTF-8 Base64 string to binary, returns 0 if
292 // the binary buffer was large enough to hold the result. The output parameter
293 // 'char_count' will contain the length of the converted string. If the output
294 // buffer is too small or any of the pointer arguments are NULL, the subroutine
295 // will return -1 and set 'char_count' to the required buffer size. No data will
296 // be written to 'buf' if the subroutine fails.
299 Kumu::base64decode(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count)
301 register byte_t c = 0, d = 0;
302 register ui32_t phase = 0, i = 0;
304 if ( str == 0 || buf == 0 || char_count == 0 )
307 while ( *str != 0 && i < buf_len )
309 c = decode_map[(int)*str++];
310 if ( c == 0xff ) continue;
311 if ( c == 0xfe ) break;
320 buf[i - 1] |= c >> 4;
325 buf[i++] = ( d << 4 ) | ( c >> 2 );
330 buf[i++] = ( d << 6 ) | c;
340 //------------------------------------------------------------------------------------------
342 // convert utf-8 hext string to bin
344 Kumu::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size)
348 KM_TEST_NULL_L(conv_size);
352 if ( str[0] == 0 ) // nothing to convert
355 for ( int j = 0; str[j]; j++ )
357 if ( isxdigit(str[j]) )
361 if ( *conv_size & 0x01 ) (*conv_size)++;
364 if ( *conv_size > buf_len )// maximum possible data size
369 int phase = 0; // track high/low nybble
371 // for each character, fill in the high nybble then the low
372 for ( int i = 0; str[i]; i++ )
374 if ( ! isxdigit(str[i]) )
377 byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) );
381 buf[*conv_size] = val << 4;
386 buf[*conv_size] |= val;
395 #ifdef CONFIG_RANDOM_UUID
397 // convert a memory region to a NULL-terminated hexadecimal string
400 bin2hex_rand(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
404 || ((bin_len * 2) + 1) > str_len )
408 Kumu::mem_ptr<byte_t> rand_buf = new byte_t[bin_len];
409 Kumu::FortunaRNG RNG;
410 RNG.FillRandom(rand_buf, bin_len);
412 for ( ui32_t i = 0; i < bin_len; i++ )
414 *p = (bin_buf[i] >> 4) & 0x0f;
415 *p += *p < 10 ? 0x30 : (( ((rand_buf[i] & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
418 *p = bin_buf[i] & 0x0f;
419 *p += *p < 10 ? 0x30 : (( (((rand_buf[i] >> 1) & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
428 // convert a memory region to a NULL-terminated hexadecimal string
431 Kumu::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
435 || ((bin_len * 2) + 1) > str_len )
438 #ifdef CONFIG_RANDOM_UUID
439 const char* use_random_uuid = getenv("KM_USE_RANDOM_UUID");
440 if ( use_random_uuid != 0 && use_random_uuid[0] != 0 && use_random_uuid[0] != '0' )
441 return bin2hex_rand(bin_buf, bin_len, str_buf, str_len);
446 for ( ui32_t i = 0; i < bin_len; i++ )
448 *p = (bin_buf[i] >> 4) & 0x0f;
449 *p += *p < 10 ? 0x30 : 0x61 - 10;
452 *p = bin_buf[i] & 0x0f;
453 *p += *p < 10 ? 0x30 : 0x61 - 10;
462 // spew a range of bin data as hex
464 Kumu::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream)
472 static ui32_t row_len = 16;
473 const byte_t* p = buf;
474 const byte_t* end_p = p + dump_len;
476 for ( ui32_t line = 0; p < end_p; line++ )
478 fprintf(stream, " %06x: ", line);
482 for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
483 fprintf(stream, "%02x ", *pp);
485 while ( i++ < row_len )
488 for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
489 fputc((isprint(*pp) ? *pp : '.'), stream);
498 Kumu::bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
502 if ( str_len < 34 || bin_len != UUID_Length )
505 if ( bin2hex(bin_buf, bin_len, str_buf, str_len) == 0 )
509 for ( k = 19, i = 12; i > 0; i-- )
510 str_buf[k+i+4] = str_buf[k+i];
512 // shift the time (mid+hi+clk)
513 for ( k = 15, j = 3; k > 6; k -= 4, j-- )
515 for ( i = 4; i > 0; i-- )
516 str_buf[k+i+j] = str_buf[k+i];
519 // add in the hyphens and trainling null
520 for ( i = 8; i < 24; i += 5 )
529 Kumu::GenRandomValue(UUID& ID)
531 byte_t tmp_buf[UUID_Length];
532 GenRandomUUID(tmp_buf);
538 Kumu::GenRandomUUID(byte_t* buf)
541 RNG.FillRandom(buf, UUID_Length);
542 buf[6] &= 0x0f; // clear bits 4-7
543 buf[6] |= 0x40; // set UUID version
544 buf[8] &= 0x3f; // clear bits 6&7
545 buf[8] |= 0x80; // set bit 7
550 Kumu::GenRandomValue(SymmetricKey& Key)
552 byte_t tmp_buf[SymmetricKey_Length];
554 RNG.FillRandom(tmp_buf, SymmetricKey_Length);
559 //------------------------------------------------------------------------------------------
560 // read a ber value from the buffer and compare with test value.
561 // Advances buffer to first character after BER value.
564 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
569 if ( ( **buf & 0x80 ) == 0 )
573 ui8_t ber_size = ( **buf & 0x0f ) + 1;
578 for ( ui8_t i = 1; i < ber_size; i++ )
581 val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
585 return ( val == test_value );
591 Kumu::read_BER(const byte_t* buf, ui64_t* val)
595 if ( buf == 0 || val == 0 )
598 if ( ( *buf & 0x80 ) == 0 )
602 ber_size = ( *buf & 0x0f ) + 1;
607 for ( i = 1; i < ber_size; i++ )
610 *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
617 static const ui64_t ber_masks[9] =
618 { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00),
619 ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
620 ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
621 ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
627 Kumu::get_BER_length_for_value(ui64_t val)
629 for ( ui32_t i = 0; i < 9; i++ )
631 if ( ( val & ber_masks[i] ) == 0 )
635 ui64Printer tmp_i(val);
636 DefaultLogSink().Error("BER integer encoding not supported for large value %s\n", tmp_i.c_str());
642 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
648 { // calculate default length
649 if ( val < 0x01000000L )
651 else if ( val < ui64_C(0x0100000000000000) )
657 { // sanity check BER length
660 DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len);
664 if ( ( val & ber_masks[ber_len - 1] ) != 0 )
666 ui64Printer tmp_i(val);
667 DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str());
672 buf[0] = 0x80 + ( ber_len - 1 );
674 for ( ui32_t i = ber_len - 1; i > 0; i-- )
676 buf[i] = (ui8_t)(val & 0xff);
684 //------------------------------------------------------------------------------------------
687 #define TIMESTAMP_TO_SYSTIME(ts, t) \
688 (t)->wYear = (ts).Year; /* year */ \
689 (t)->wMonth = (ts).Month; /* month of year (1 - 12) */ \
690 (t)->wDay = (ts).Day; /* day of month (1 - 31) */ \
691 (t)->wHour = (ts).Hour; /* hours (0 - 23) */ \
692 (t)->wMinute = (ts).Minute; /* minutes (0 - 59) */ \
693 (t)->wSecond = (ts).Second; /* seconds (0 - 60) */ \
694 (t)->wDayOfWeek = 0; \
695 (t)->wMilliseconds = 0
697 #define SYSTIME_TO_TIMESTAMP(t, ts) \
698 (ts).Year = (t)->wYear; /* year */ \
699 (ts).Month = (t)->wMonth; /* month of year (1 - 12) */ \
700 (ts).Day = (t)->wDay; /* day of month (1 - 31) */ \
701 (ts).Hour = (t)->wHour; /* hours (0 - 23) */ \
702 (ts).Minute = (t)->wMinute; /* minutes (0 - 59) */ \
703 (ts).Second = (t)->wSecond; /* seconds (0 - 60) */
706 Kumu::Timestamp::Timestamp() :
707 Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
710 GetSystemTime(&sys_time);
711 SYSTIME_TO_TIMESTAMP(&sys_time, *this);
716 Kumu::Timestamp::operator<(const Timestamp& rhs) const
718 SYSTEMTIME lhst, rhst;
721 TIMESTAMP_TO_SYSTIME(*this, &lhst);
722 TIMESTAMP_TO_SYSTIME(rhs, &rhst);
723 SystemTimeToFileTime(&lhst, &lft);
724 SystemTimeToFileTime(&rhst, &rft);
725 return ( CompareFileTime(&lft, &rft) == -1 );
730 Kumu::Timestamp::operator>(const Timestamp& rhs) const
732 SYSTEMTIME lhst, rhst;
735 TIMESTAMP_TO_SYSTIME(*this, &lhst);
736 TIMESTAMP_TO_SYSTIME(rhs, &rhst);
737 SystemTimeToFileTime(&lhst, &lft);
738 SystemTimeToFileTime(&rhst, &rft);
739 return ( CompareFileTime(&lft, &rft) == 1 );
743 seconds_to_ns100(ui32_t seconds)
745 return ((ui64_t)seconds * 10000000);
750 Kumu::Timestamp::AddDays(i32_t days)
752 SYSTEMTIME current_st;
754 ULARGE_INTEGER current_ul;
758 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
759 SystemTimeToFileTime(¤t_st, ¤t_ft);
760 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
761 current_ul.QuadPart += ( seconds_to_ns100(86400) * (i64_t)days );
762 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
763 FileTimeToSystemTime(¤t_ft, ¤t_st);
764 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
770 Kumu::Timestamp::AddHours(i32_t hours)
772 SYSTEMTIME current_st;
774 ULARGE_INTEGER current_ul;
778 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
779 SystemTimeToFileTime(¤t_st, ¤t_ft);
780 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
781 current_ul.QuadPart += ( seconds_to_ns100(3600) * (i64_t)hours );
782 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
783 FileTimeToSystemTime(¤t_ft, ¤t_st);
784 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
790 Kumu::Timestamp::AddMinutes(i32_t minutes)
792 SYSTEMTIME current_st;
794 ULARGE_INTEGER current_ul;
798 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
799 SystemTimeToFileTime(¤t_st, ¤t_ft);
800 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
801 current_ul.QuadPart += ( seconds_to_ns100(60) * (i64_t)minutes );
802 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
803 FileTimeToSystemTime(¤t_ft, ¤t_st);
804 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
810 Kumu::Timestamp::AddSeconds(i32_t seconds)
812 SYSTEMTIME current_st;
814 ULARGE_INTEGER current_ul;
818 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
819 SystemTimeToFileTime(¤t_st, ¤t_ft);
820 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
821 current_ul.QuadPart += ( seconds_to_ns100(1) * (i64_t)seconds );
822 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
823 FileTimeToSystemTime(¤t_ft, ¤t_st);
824 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
832 #define TIMESTAMP_TO_CALTIME(ts, ct) \
833 (ct)->date.year = (ts).Year; /* year */ \
834 (ct)->date.month = (ts).Month; /* month of year (1 - 12) */ \
835 (ct)->date.day = (ts).Day; /* day of month (1 - 31) */ \
836 (ct)->hour = (ts).Hour; /* hours (0 - 23) */ \
837 (ct)->minute = (ts).Minute; /* minutes (0 - 59) */ \
838 (ct)->second = (ts).Second; /* seconds (0 - 60) */ \
841 #define CALTIME_TO_TIMESTAMP(ct, ts) \
842 assert((ct)->offset == 0); \
843 (ts).Year = (ct)->date.year; /* year */ \
844 (ts).Month = (ct)->date.month; /* month of year (1 - 12) */ \
845 (ts).Day = (ct)->date.day; /* day of month (1 - 31) */ \
846 (ts).Hour = (ct)->hour; /* hours (0 - 23) */ \
847 (ts).Minute = (ct)->minute; /* minutes (0 - 59) */ \
848 (ts).Second = (ct)->second; /* seconds (0 - 60) */
852 Kumu::Timestamp::Timestamp() :
853 Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
856 Kumu::TAI::caltime ct;
859 CALTIME_TO_TIMESTAMP(&ct, *this)
864 Kumu::Timestamp::operator<(const Timestamp& rhs) const
866 Kumu::TAI::caltime lh_ct, rh_ct;
867 TIMESTAMP_TO_CALTIME(*this, &lh_ct)
868 TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
870 Kumu::TAI::tai lh_tai, rh_tai;
874 return ( lh_tai.x < rh_tai.x );
879 Kumu::Timestamp::operator>(const Timestamp& rhs) const
881 Kumu::TAI::caltime lh_ct, rh_ct;
882 TIMESTAMP_TO_CALTIME(*this, &lh_ct)
883 TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
885 Kumu::TAI::tai lh_tai, rh_tai;
889 return ( lh_tai.x > rh_tai.x );
894 Kumu::Timestamp::AddDays(i32_t days)
896 Kumu::TAI::caltime ct;
901 TIMESTAMP_TO_CALTIME(*this, &ct)
905 CALTIME_TO_TIMESTAMP(&ct, *this)
911 Kumu::Timestamp::AddHours(i32_t hours)
913 Kumu::TAI::caltime ct;
918 TIMESTAMP_TO_CALTIME(*this, &ct)
922 CALTIME_TO_TIMESTAMP(&ct, *this)
928 Kumu::Timestamp::AddMinutes(i32_t minutes)
930 Kumu::TAI::caltime ct;
935 TIMESTAMP_TO_CALTIME(*this, &ct)
937 t.add_minutes(minutes);
939 CALTIME_TO_TIMESTAMP(&ct, *this)
945 Kumu::Timestamp::AddSeconds(i32_t seconds)
947 Kumu::TAI::caltime ct;
952 TIMESTAMP_TO_CALTIME(*this, &ct)
954 t.add_seconds(seconds);
956 CALTIME_TO_TIMESTAMP(&ct, *this)
963 Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
974 Kumu::Timestamp::Timestamp(const char* datestr) : IArchive()
976 if ( ! DecodeString(datestr) )
983 Kumu::Timestamp::~Timestamp()
988 const Kumu::Timestamp&
989 Kumu::Timestamp::operator=(const Timestamp& rhs)
1002 Kumu::Timestamp::operator==(const Timestamp& rhs) const
1004 if ( Year == rhs.Year
1005 && Month == rhs.Month
1008 && Minute == rhs.Minute
1009 && Second == rhs.Second )
1017 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
1019 if ( Year != rhs.Year
1020 || Month != rhs.Month
1023 || Minute != rhs.Minute
1024 || Second != rhs.Second )
1032 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
1034 return EncodeStringWithOffset(str_buf, buf_len, 0);
1039 Kumu::Timestamp::EncodeStringWithOffset(char* str_buf, ui32_t buf_len,
1040 i32_t offset_minutes) const
1042 if ( buf_len < ( DateTimeLen + 1 ) )
1045 // ensure offset is within +/- 14 hours
1046 if ((offset_minutes < -14 * 60) || (offset_minutes > 14 * 60))
1049 // set the apparent time
1050 Kumu::Timestamp tmp_t(*this);
1051 tmp_t.AddMinutes(offset_minutes);
1053 char direction = '+';
1054 if (offset_minutes < 0) {
1056 // need absolute offset from zero
1057 offset_minutes = -offset_minutes;
1060 // 2004-05-01T13:20:00+00:00
1061 snprintf(str_buf, buf_len,
1062 "%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02hu:%02hu",
1063 tmp_t.Year, tmp_t.Month, tmp_t.Day,
1064 tmp_t.Hour, tmp_t.Minute, tmp_t.Second,
1066 offset_minutes / 60,
1067 offset_minutes % 60);
1074 Kumu::Timestamp::DecodeString(const char* datestr)
1078 if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
1079 || datestr[4] != '-'
1080 || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
1081 || datestr[7] != '-'
1082 || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
1085 ui32_t char_count = 10;
1086 TmpStamp.Year = atoi(datestr);
1087 TmpStamp.Month = atoi(datestr + 5);
1088 TmpStamp.Day = atoi(datestr + 8);
1089 TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
1091 if ( datestr[10] == 'T' )
1093 if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
1094 || datestr[13] != ':'
1095 || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
1099 TmpStamp.Hour = atoi(datestr + 11);
1100 TmpStamp.Minute = atoi(datestr + 14);
1102 if ( datestr[16] == ':' )
1104 if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
1108 TmpStamp.Second = atoi(datestr + 17);
1111 if ( datestr[19] == '.' )
1113 if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
1116 // we don't carry the ms value
1120 if ( datestr[19] == '-' || datestr[19] == '+' )
1122 if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
1123 || datestr[22] != ':'
1124 || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
1129 ui32_t TZ_hh = atoi(datestr + 20);
1130 ui32_t TZ_mm = atoi(datestr + 23);
1131 if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
1134 i32_t TZ_offset = 60 * TZ_hh + TZ_mm;
1135 if (datestr[19] == '-')
1136 TZ_offset = -TZ_offset;
1137 /* at this point, TZ_offset reflects the contents of the string */
1139 /* a negative offset is behind UTC and so needs to increment to
1140 * convert, while a positive offset must do the reverse */
1141 TmpStamp.AddMinutes(-TZ_offset);
1143 else if (datestr[19] == 'Z')
1145 /* act as if the offset were +00:00 */
1150 if ( datestr[char_count] != 0 )
1152 Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
1153 datestr, char_count);
1160 TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
1161 if ( SystemTimeToFileTime(&st, &ft) == 0 )
1163 SYSTIME_TO_TIMESTAMP(&st, *this);
1166 Kumu::TAI::caltime ct;
1167 TIMESTAMP_TO_CALTIME(TmpStamp, &ct);
1168 t = ct; // back and forth to tai to normalize offset
1170 CALTIME_TO_TIMESTAMP(&ct, *this)
1178 Kumu::Timestamp::HasValue() const
1180 if ( Year || Month || Day || Hour || Minute || Second )
1188 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
1191 if ( ! Reader->ReadUi16BE(&Year) ) return false;
1192 if ( ! Reader->ReadRaw(&Month, 6) ) return false;
1198 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
1201 if ( ! Writer->WriteUi16BE(Year) ) return false;
1202 if ( ! Writer->WriteRaw(&Month, 6) ) return false;
1208 Kumu::Timestamp::GetSecondsSinceEpoch(void) const
1212 TIMESTAMP_TO_SYSTIME(*this, &timeST);
1214 SystemTimeToFileTime(&timeST, &timeFT);
1215 ULARGE_INTEGER timeUL;
1216 timeUL.LowPart = timeFT.dwLowDateTime;
1217 timeUL.HighPart = timeFT.dwHighDateTime;
1220 epochST.wYear = 1970;
1222 epochST.wDayOfWeek = 4;
1225 epochST.wMinute = 0;
1226 epochST.wSecond = 0;
1227 epochST.wMilliseconds = 0;
1229 SystemTimeToFileTime(&epochST, &epochFT);
1230 ULARGE_INTEGER epochUL;
1231 epochUL.LowPart = epochFT.dwLowDateTime;
1232 epochUL.HighPart = epochFT.dwHighDateTime;
1234 return (timeUL.QuadPart - epochUL.QuadPart) / 10000000;
1236 Kumu::TAI::caltime ct;
1238 TIMESTAMP_TO_CALTIME(*this, &ct);
1241 return (long) (t.x - ui64_C(4611686018427387914));
1245 //------------------------------------------------------------------------------------------
1247 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
1248 : m_p(0), m_capacity(0), m_size(0)
1251 m_capacity = Buf->Capacity();
1252 assert(m_p); assert(m_capacity);
1256 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
1258 if ( ( m_size + ber_len ) > m_capacity )
1261 if ( ! write_BER(m_p + m_size, i, ber_len) )
1269 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1270 : m_p(0), m_capacity(0), m_size(0)
1272 m_p = Buf->RoData();
1273 m_capacity = Buf->Length();
1274 assert(m_p); assert(m_capacity);
1278 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1280 if ( i == 0 || ber_len == 0 ) return false;
1282 if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1285 if ( ( m_size + *ber_len ) > m_capacity )
1288 if ( ! read_BER(m_p + m_size, i) )
1295 //------------------------------------------------------------------------------------------
1297 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1299 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1304 Kumu::ByteString::~ByteString()
1311 // copy the given data into the ByteString, set Length value.
1312 // Returns error if the ByteString is too small.
1314 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1316 if ( m_Capacity < buf_len )
1317 return RESULT_ALLOC;
1319 memcpy(m_Data, buf, buf_len);
1325 // copy the given data into the ByteString, set Length value.
1326 // Returns error if the ByteString is too small.
1328 Kumu::ByteString::Set(const ByteString& Buf)
1330 if ( m_Capacity < Buf.m_Capacity )
1331 return RESULT_ALLOC;
1333 memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1334 m_Length = Buf.m_Length;
1339 // Sets the size of the internally allocate buffer.
1341 Kumu::ByteString::Capacity(ui32_t cap_size)
1343 if ( m_Capacity >= cap_size )
1346 byte_t* tmp_data = 0;
1355 if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1356 return RESULT_ALLOC;
1358 if ( tmp_data != 0 )
1360 assert(m_Length > 0);
1361 memcpy(m_Data, tmp_data, m_Length);
1365 m_Capacity = cap_size;
1371 Kumu::ByteString::Append(const ByteString& Buf)
1373 Result_t result = RESULT_OK;
1374 ui32_t diff = m_Capacity - m_Length;
1376 if ( diff < Buf.Length() )
1377 result = Capacity(m_Capacity + Buf.Length());
1379 if ( KM_SUCCESS(result) )
1381 memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1382 m_Length += Buf.Length();
1390 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1392 Result_t result = RESULT_OK;
1393 ui32_t diff = m_Capacity - m_Length;
1395 if ( diff < buf_len )
1396 result = Capacity(m_Capacity + buf_len);
1398 if ( KM_SUCCESS(result) )
1400 memcpy(m_Data + m_Length, buf, buf_len);
1401 m_Length += buf_len;