2 Copyright (c) 2005-2010, 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;
61 const ui32_t MapMax = 2048;
63 static Kumu::Mutex s_MapLock;
64 static ui32_t s_MapSize = 0;
65 static struct map_entry_t s_ResultMap[MapMax];
70 Kumu::Result_t::Find(int v)
75 AutoMutex L(s_MapLock);
77 for ( ui32_t i = 0; i < s_MapSize; ++i )
79 if ( s_ResultMap[i].rcode == v )
80 return *s_ResultMap[i].result;
83 return RESULT_UNKNOWN;
88 Kumu::Result_t::Delete(int v)
90 if ( v < -99 || v > 99 )
92 DefaultLogSink().Error("Cannot delete core result code: %ld\n", v);
96 AutoMutex L(s_MapLock);
98 for ( ui32_t i = 0; i < s_MapSize; ++i )
100 if ( s_ResultMap[i].rcode == v )
102 for ( ++i; i < s_MapSize; ++i )
103 s_ResultMap[i-1] = s_ResultMap[i];
115 Kumu::Result_t::End()
121 const Kumu::Result_t&
122 Kumu::Result_t::Get(unsigned int i)
124 return *s_ResultMap[i].result;
128 Kumu::Result_t::Result_t(int v, const char* s, const char* l) : value(v), symbol(s), label(l)
136 AutoMutex L(s_MapLock);
138 for ( ui32_t i = 0; i < s_MapSize; ++i )
140 if ( s_ResultMap[i].rcode == v )
144 assert(s_MapSize+1 < MapMax);
146 s_ResultMap[s_MapSize].rcode = v;
147 s_ResultMap[s_MapSize].result = this;
153 Kumu::Result_t::~Result_t() {}
156 //------------------------------------------------------------------------------------------
159 static int s_DTraceSequence = 0;
161 Kumu::DTrace_t::DTrace_t(const char* Label, Kumu::Result_t* Watch, int Line, const char* File)
162 : m_Label(Label), m_Watch(Watch), m_Line(Line), m_File(File)
164 m_Sequence = s_DTraceSequence++;
165 DefaultLogSink().Debug("@enter %s[%d] (%s at %d)\n", m_Label, m_Sequence, m_File, m_Line);
168 Kumu::DTrace_t::~DTrace_t()
171 DefaultLogSink().Debug("@exit %s[%d]: %s\n", m_Label, m_Sequence, m_Watch->Label());
173 DefaultLogSink().Debug("@exit %s[%d]\n", m_Label, m_Sequence);
176 //------------------------------------------------------------------------------------------
179 const char fill = '=';
180 const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
182 const byte_t decode_map[] =
183 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
184 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
185 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
186 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
187 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
188 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63,
189 52, 53, 54, 55, 56, 57, 58, 59,
190 60, 61, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
191 0xff, 0, 1, 2, 3, 4, 5, 6,
192 7, 8, 9, 10, 11, 12, 13, 14,
193 15, 16, 17, 18, 19, 20, 21, 22,
194 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff,
195 0xff, 26, 27, 28, 29, 30, 31, 32,
196 33, 34, 35, 36, 37, 38, 39, 40,
197 41, 42, 43, 44, 45, 46, 47, 48,
198 49, 50, 51, 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, 0xff, 0xff, 0xff, 0xff, 0xff,
203 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
204 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
205 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
206 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
207 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
208 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
209 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
210 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
211 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
212 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
213 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
214 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
218 // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer
219 // if the binary buffer was large enough to hold the result. If the output buffer
220 // is too small or any of the pointer arguments are NULL, the subroutine will
224 Kumu::base64encode(const byte_t* buf, ui32_t buf_len, char* strbuf, ui32_t strbuf_len)
227 ui32_t i, block_len, diff;
229 if ( buf == 0 || strbuf == 0 )
232 if ( strbuf_len < base64_encode_length(buf_len) + 1 )
237 while ( block_len % 3 )
240 for ( i = 0; i < block_len; i += 3 )
242 strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
243 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
244 strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) | ( buf[2] >> 6 ) )];
245 strbuf[out_char++] = base64_chars[( buf[2] & 0x3f )];
255 strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
259 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) )];
260 strbuf[out_char++] = fill;
262 else if ( diff == 2 )
264 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
265 strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) )];
268 strbuf[out_char++] = fill;
271 strbuf[out_char] = 0;
278 // Convert NULL-terminated UTF-8 Base64 string to binary, returns 0 if
279 // the binary buffer was large enough to hold the result. The output parameter
280 // 'char_count' will contain the length of the converted string. If the output
281 // buffer is too small or any of the pointer arguments are NULL, the subroutine
282 // will return -1 and set 'char_count' to the required buffer size. No data will
283 // be written to 'buf' if the subroutine fails.
286 Kumu::base64decode(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count)
288 register byte_t c = 0, d = 0;
289 register ui32_t phase = 0, i = 0;
291 if ( str == 0 || buf == 0 || char_count == 0 )
294 while ( *str != 0 && i < buf_len )
296 c = decode_map[(int)*str++];
297 if ( c == 0xff ) continue;
298 if ( c == 0xfe ) break;
307 buf[i - 1] |= c >> 4;
312 buf[i++] = ( d << 4 ) | ( c >> 2 );
317 buf[i++] = ( d << 6 ) | c;
327 //------------------------------------------------------------------------------------------
329 // convert utf-8 hext string to bin
331 Kumu::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size)
335 KM_TEST_NULL_L(conv_size);
339 if ( str[0] == 0 ) // nothing to convert
342 for ( int j = 0; str[j]; j++ )
344 if ( isxdigit(str[j]) )
348 if ( *conv_size & 0x01 ) (*conv_size)++;
351 if ( *conv_size > buf_len )// maximum possible data size
356 int phase = 0; // track high/low nybble
358 // for each character, fill in the high nybble then the low
359 for ( int i = 0; str[i]; i++ )
361 if ( ! isxdigit(str[i]) )
364 byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) );
368 buf[*conv_size] = val << 4;
373 buf[*conv_size] |= val;
382 #ifdef CONFIG_RANDOM_UUID
384 // convert a memory region to a NULL-terminated hexadecimal string
387 bin2hex_rand(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
391 || ((bin_len * 2) + 1) > str_len )
395 Kumu::mem_ptr<byte_t> rand_buf = new byte_t[bin_len];
396 Kumu::FortunaRNG RNG;
397 RNG.FillRandom(rand_buf, bin_len);
399 for ( ui32_t i = 0; i < bin_len; i++ )
401 *p = (bin_buf[i] >> 4) & 0x0f;
402 *p += *p < 10 ? 0x30 : (( ((rand_buf[i] & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
405 *p = bin_buf[i] & 0x0f;
406 *p += *p < 10 ? 0x30 : (( (((rand_buf[i] >> 1) & 0x01) == 0) ? 0x61 : 0x41 ) - 10);
415 // convert a memory region to a NULL-terminated hexadecimal string
418 Kumu::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
422 || ((bin_len * 2) + 1) > str_len )
425 #ifdef CONFIG_RANDOM_UUID
426 const char* use_random_uuid = getenv("KM_USE_RANDOM_UUID");
427 if ( use_random_uuid != 0 && use_random_uuid[0] != 0 && use_random_uuid[0] != '0' )
428 return bin2hex_rand(bin_buf, bin_len, str_buf, str_len);
433 for ( ui32_t i = 0; i < bin_len; i++ )
435 *p = (bin_buf[i] >> 4) & 0x0f;
436 *p += *p < 10 ? 0x30 : 0x61 - 10;
439 *p = bin_buf[i] & 0x0f;
440 *p += *p < 10 ? 0x30 : 0x61 - 10;
449 // spew a range of bin data as hex
451 Kumu::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream)
459 static ui32_t row_len = 16;
460 const byte_t* p = buf;
461 const byte_t* end_p = p + dump_len;
463 for ( ui32_t line = 0; p < end_p; line++ )
465 fprintf(stream, " %06x: ", line);
469 for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
470 fprintf(stream, "%02x ", *pp);
472 while ( i++ < row_len )
475 for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
476 fputc((isprint(*pp) ? *pp : '.'), stream);
485 Kumu::bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
489 if ( str_len < 34 || bin_len != UUID_Length )
492 if ( bin2hex(bin_buf, bin_len, str_buf, str_len) == 0 )
496 for ( k = 19, i = 12; i > 0; i-- )
497 str_buf[k+i+4] = str_buf[k+i];
499 // shift the time (mid+hi+clk)
500 for ( k = 15, j = 3; k > 6; k -= 4, j-- )
502 for ( i = 4; i > 0; i-- )
503 str_buf[k+i+j] = str_buf[k+i];
506 // add in the hyphens and trainling null
507 for ( i = 8; i < 24; i += 5 )
516 Kumu::GenRandomValue(UUID& ID)
518 byte_t tmp_buf[UUID_Length];
519 GenRandomUUID(tmp_buf);
525 Kumu::GenRandomUUID(byte_t* buf)
528 RNG.FillRandom(buf, UUID_Length);
529 buf[6] &= 0x0f; // clear bits 4-7
530 buf[6] |= 0x40; // set UUID version
531 buf[8] &= 0x3f; // clear bits 6&7
532 buf[8] |= 0x80; // set bit 7
537 Kumu::GenRandomValue(SymmetricKey& Key)
539 byte_t tmp_buf[SymmetricKey_Length];
541 RNG.FillRandom(tmp_buf, SymmetricKey_Length);
546 //------------------------------------------------------------------------------------------
547 // read a ber value from the buffer and compare with test value.
548 // Advances buffer to first character after BER value.
551 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
556 if ( ( **buf & 0x80 ) == 0 )
560 ui8_t ber_size = ( **buf & 0x0f ) + 1;
565 for ( ui8_t i = 1; i < ber_size; i++ )
568 val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
572 return ( val == test_value );
578 Kumu::read_BER(const byte_t* buf, ui64_t* val)
582 if ( buf == 0 || val == 0 )
585 if ( ( *buf & 0x80 ) == 0 )
589 ber_size = ( *buf & 0x0f ) + 1;
594 for ( i = 1; i < ber_size; i++ )
597 *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
604 static const ui64_t ber_masks[9] =
605 { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00),
606 ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
607 ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
608 ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
614 Kumu::get_BER_length_for_value(ui64_t val)
616 for ( ui32_t i = 0; i < 9; i++ )
618 if ( ( val & ber_masks[i] ) == 0 )
622 ui64Printer tmp_i(val);
623 DefaultLogSink().Error("BER integer encoding not supported for large value %s\n", tmp_i.c_str());
629 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
635 { // calculate default length
636 if ( val < 0x01000000L )
638 else if ( val < ui64_C(0x0100000000000000) )
644 { // sanity check BER length
647 DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len);
651 if ( ( val & ber_masks[ber_len - 1] ) != 0 )
653 ui64Printer tmp_i(val);
654 DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str());
659 buf[0] = 0x80 + ( ber_len - 1 );
661 for ( ui32_t i = ber_len - 1; i > 0; i-- )
663 buf[i] = (ui8_t)(val & 0xff);
671 //------------------------------------------------------------------------------------------
674 #define TIMESTAMP_TO_SYSTIME(ts, t) \
675 (t)->wYear = (ts).Year; /* year */ \
676 (t)->wMonth = (ts).Month; /* month of year (1 - 12) */ \
677 (t)->wDay = (ts).Day; /* day of month (1 - 31) */ \
678 (t)->wHour = (ts).Hour; /* hours (0 - 23) */ \
679 (t)->wMinute = (ts).Minute; /* minutes (0 - 59) */ \
680 (t)->wSecond = (ts).Second; /* seconds (0 - 60) */ \
681 (t)->wDayOfWeek = 0; \
682 (t)->wMilliseconds = 0
684 #define SYSTIME_TO_TIMESTAMP(t, ts) \
685 (ts).Year = (t)->wYear; /* year */ \
686 (ts).Month = (t)->wMonth; /* month of year (1 - 12) */ \
687 (ts).Day = (t)->wDay; /* day of month (1 - 31) */ \
688 (ts).Hour = (t)->wHour; /* hours (0 - 23) */ \
689 (ts).Minute = (t)->wMinute; /* minutes (0 - 59) */ \
690 (ts).Second = (t)->wSecond; /* seconds (0 - 60) */
693 Kumu::Timestamp::Timestamp() :
694 Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
697 GetSystemTime(&sys_time);
698 SYSTIME_TO_TIMESTAMP(&sys_time, *this);
703 Kumu::Timestamp::operator<(const Timestamp& rhs) const
705 SYSTEMTIME lhst, rhst;
708 TIMESTAMP_TO_SYSTIME(*this, &lhst);
709 TIMESTAMP_TO_SYSTIME(rhs, &rhst);
710 SystemTimeToFileTime(&lhst, &lft);
711 SystemTimeToFileTime(&rhst, &rft);
712 return ( CompareFileTime(&lft, &rft) == -1 );
717 Kumu::Timestamp::operator>(const Timestamp& rhs) const
719 SYSTEMTIME lhst, rhst;
722 TIMESTAMP_TO_SYSTIME(*this, &lhst);
723 TIMESTAMP_TO_SYSTIME(rhs, &rhst);
724 SystemTimeToFileTime(&lhst, &lft);
725 SystemTimeToFileTime(&rhst, &rft);
726 return ( CompareFileTime(&lft, &rft) == 1 );
730 seconds_to_ns100(ui32_t seconds)
732 return ((ui64_t)seconds * 10000000);
737 Kumu::Timestamp::AddDays(i32_t days)
739 SYSTEMTIME current_st;
741 ULARGE_INTEGER current_ul;
745 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
746 SystemTimeToFileTime(¤t_st, ¤t_ft);
747 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
748 current_ul.QuadPart += ( seconds_to_ns100(86400) * (i64_t)days );
749 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
750 FileTimeToSystemTime(¤t_ft, ¤t_st);
751 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
757 Kumu::Timestamp::AddHours(i32_t hours)
759 SYSTEMTIME current_st;
761 ULARGE_INTEGER current_ul;
765 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
766 SystemTimeToFileTime(¤t_st, ¤t_ft);
767 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
768 current_ul.QuadPart += ( seconds_to_ns100(3600) * (i64_t)hours );
769 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
770 FileTimeToSystemTime(¤t_ft, ¤t_st);
771 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
777 Kumu::Timestamp::AddMinutes(i32_t minutes)
779 SYSTEMTIME current_st;
781 ULARGE_INTEGER current_ul;
785 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
786 SystemTimeToFileTime(¤t_st, ¤t_ft);
787 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
788 current_ul.QuadPart += ( seconds_to_ns100(60) * (i64_t)minutes );
789 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
790 FileTimeToSystemTime(¤t_ft, ¤t_st);
791 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
797 Kumu::Timestamp::AddSeconds(i32_t seconds)
799 SYSTEMTIME current_st;
801 ULARGE_INTEGER current_ul;
805 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
806 SystemTimeToFileTime(¤t_st, ¤t_ft);
807 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
808 current_ul.QuadPart += ( seconds_to_ns100(1) * (i64_t)seconds );
809 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
810 FileTimeToSystemTime(¤t_ft, ¤t_st);
811 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
819 #define TIMESTAMP_TO_CALTIME(ts, ct) \
820 (ct)->date.year = (ts).Year; /* year */ \
821 (ct)->date.month = (ts).Month; /* month of year (1 - 12) */ \
822 (ct)->date.day = (ts).Day; /* day of month (1 - 31) */ \
823 (ct)->hour = (ts).Hour; /* hours (0 - 23) */ \
824 (ct)->minute = (ts).Minute; /* minutes (0 - 59) */ \
825 (ct)->second = (ts).Second; /* seconds (0 - 60) */ \
828 #define CALTIME_TO_TIMESTAMP(ct, ts) \
829 assert((ct)->offset == 0); \
830 (ts).Year = (ct)->date.year; /* year */ \
831 (ts).Month = (ct)->date.month; /* month of year (1 - 12) */ \
832 (ts).Day = (ct)->date.day; /* day of month (1 - 31) */ \
833 (ts).Hour = (ct)->hour; /* hours (0 - 23) */ \
834 (ts).Minute = (ct)->minute; /* minutes (0 - 59) */ \
835 (ts).Second = (ct)->second; /* seconds (0 - 60) */
839 Kumu::Timestamp::Timestamp() :
840 Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
843 Kumu::TAI::caltime ct;
846 CALTIME_TO_TIMESTAMP(&ct, *this)
851 Kumu::Timestamp::operator<(const Timestamp& rhs) const
853 Kumu::TAI::caltime lh_ct, rh_ct;
854 TIMESTAMP_TO_CALTIME(*this, &lh_ct)
855 TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
857 Kumu::TAI::tai lh_tai, rh_tai;
861 return ( lh_tai.x < rh_tai.x );
866 Kumu::Timestamp::operator>(const Timestamp& rhs) const
868 Kumu::TAI::caltime lh_ct, rh_ct;
869 TIMESTAMP_TO_CALTIME(*this, &lh_ct)
870 TIMESTAMP_TO_CALTIME(rhs, &rh_ct)
872 Kumu::TAI::tai lh_tai, rh_tai;
876 return ( lh_tai.x > rh_tai.x );
881 Kumu::Timestamp::AddDays(i32_t days)
883 Kumu::TAI::caltime ct;
888 TIMESTAMP_TO_CALTIME(*this, &ct)
892 CALTIME_TO_TIMESTAMP(&ct, *this)
898 Kumu::Timestamp::AddHours(i32_t hours)
900 Kumu::TAI::caltime ct;
905 TIMESTAMP_TO_CALTIME(*this, &ct)
909 CALTIME_TO_TIMESTAMP(&ct, *this)
915 Kumu::Timestamp::AddMinutes(i32_t minutes)
917 Kumu::TAI::caltime ct;
922 TIMESTAMP_TO_CALTIME(*this, &ct)
924 t.add_minutes(minutes);
926 CALTIME_TO_TIMESTAMP(&ct, *this)
932 Kumu::Timestamp::AddSeconds(i32_t seconds)
934 Kumu::TAI::caltime ct;
939 TIMESTAMP_TO_CALTIME(*this, &ct)
941 t.add_seconds(seconds);
943 CALTIME_TO_TIMESTAMP(&ct, *this)
950 Kumu::Timestamp::Timestamp(const Timestamp& rhs) : IArchive()
960 Kumu::Timestamp::~Timestamp()
965 const Kumu::Timestamp&
966 Kumu::Timestamp::operator=(const Timestamp& rhs)
979 Kumu::Timestamp::operator==(const Timestamp& rhs) const
981 if ( Year == rhs.Year
982 && Month == rhs.Month
985 && Minute == rhs.Minute
986 && Second == rhs.Second )
994 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
996 if ( Year != rhs.Year
997 || Month != rhs.Month
1000 || Minute != rhs.Minute
1001 || Second != rhs.Second )
1009 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
1011 return EncodeStringWithOffset(str_buf, buf_len, 0);
1016 Kumu::Timestamp::EncodeStringWithOffset(char* str_buf, ui32_t buf_len,
1017 i32_t offset_minutes) const
1019 if ( buf_len < ( DateTimeLen + 1 ) )
1022 // ensure offset is within +/- 14 hours
1023 if ((offset_minutes < -14 * 60) || (offset_minutes > 14 * 60))
1026 // set the apparent time
1027 Kumu::Timestamp tmp_t(*this);
1028 tmp_t.AddMinutes(offset_minutes);
1030 char direction = '+';
1031 if (offset_minutes < 0) {
1033 // need absolute offset from zero
1034 offset_minutes = -offset_minutes;
1037 // 2004-05-01T13:20:00+00:00
1038 snprintf(str_buf, buf_len,
1039 "%04hu-%02hu-%02huT%02hu:%02hu:%02hu%c%02hu:%02hu",
1040 tmp_t.Year, tmp_t.Month, tmp_t.Day,
1041 tmp_t.Hour, tmp_t.Minute, tmp_t.Second,
1043 offset_minutes / 60,
1044 offset_minutes % 60);
1051 Kumu::Timestamp::DecodeString(const char* datestr)
1055 if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) )
1056 || datestr[4] != '-'
1057 || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) )
1058 || datestr[7] != '-'
1059 || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) )
1062 ui32_t char_count = 10;
1063 TmpStamp.Year = atoi(datestr);
1064 TmpStamp.Month = atoi(datestr + 5);
1065 TmpStamp.Day = atoi(datestr + 8);
1066 TmpStamp.Hour = TmpStamp.Minute = TmpStamp.Second = 0;
1068 if ( datestr[10] == 'T' )
1070 if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) )
1071 || datestr[13] != ':'
1072 || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) )
1076 TmpStamp.Hour = atoi(datestr + 11);
1077 TmpStamp.Minute = atoi(datestr + 14);
1079 if ( datestr[16] == ':' )
1081 if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) )
1085 TmpStamp.Second = atoi(datestr + 17);
1088 if ( datestr[19] == '.' )
1090 if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
1093 // we don't carry the ms value
1097 if ( datestr[19] == '-' || datestr[19] == '+' )
1099 if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) )
1100 || datestr[22] != ':'
1101 || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) )
1106 ui32_t TZ_hh = atoi(datestr + 20);
1107 ui32_t TZ_mm = atoi(datestr + 23);
1108 if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
1111 i32_t TZ_offset = 60 * TZ_hh + TZ_mm;
1112 if (datestr[19] == '-')
1113 TZ_offset = -TZ_offset;
1114 /* at this point, TZ_offset reflects the contents of the string */
1116 /* a negative offset is behind UTC and so needs to increment to
1117 * convert, while a positive offset must do the reverse */
1118 TmpStamp.AddMinutes(-TZ_offset);
1120 else if (datestr[19] == 'Z')
1122 /* act as if the offset were +00:00 */
1127 if ( datestr[char_count] != 0 )
1129 Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n",
1130 datestr, char_count);
1137 TIMESTAMP_TO_SYSTIME(TmpStamp, &st);
1138 if ( SystemTimeToFileTime(&st, &ft) == 0 )
1140 SYSTIME_TO_TIMESTAMP(&st, *this);
1143 Kumu::TAI::caltime ct;
1144 TIMESTAMP_TO_CALTIME(TmpStamp, &ct);
1145 t = ct; // back and forth to tai to normalize offset
1147 CALTIME_TO_TIMESTAMP(&ct, *this)
1155 Kumu::Timestamp::HasValue() const
1157 if ( Year || Month || Day || Hour || Minute || Second )
1165 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
1168 if ( ! Reader->ReadUi16BE(&Year) ) return false;
1169 if ( ! Reader->ReadRaw(&Month, 6) ) return false;
1175 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
1178 if ( ! Writer->WriteUi16BE(Year) ) return false;
1179 if ( ! Writer->WriteRaw(&Month, 6) ) return false;
1185 Kumu::Timestamp::GetSecondsSinceEpoch(void) const
1189 TIMESTAMP_TO_SYSTIME(*this, &timeST);
1191 SystemTimeToFileTime(&timeST, &timeFT);
1192 ULARGE_INTEGER timeUL;
1193 timeUL.LowPart = timeFT.dwLowDateTime;
1194 timeUL.HighPart = timeFT.dwHighDateTime;
1197 epochST.wYear = 1970;
1199 epochST.wDayOfWeek = 4;
1202 epochST.wMinute = 0;
1203 epochST.wSecond = 0;
1204 epochST.wMilliseconds = 0;
1206 SystemTimeToFileTime(&epochST, &epochFT);
1207 ULARGE_INTEGER epochUL;
1208 epochUL.LowPart = epochFT.dwLowDateTime;
1209 epochUL.HighPart = epochFT.dwHighDateTime;
1211 return (timeUL.QuadPart - epochUL.QuadPart) / 10000000;
1213 Kumu::TAI::caltime ct;
1215 TIMESTAMP_TO_CALTIME(*this, &ct);
1218 return (long) (t.x - ui64_C(4611686018427387914));
1222 //------------------------------------------------------------------------------------------
1224 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
1225 : m_p(0), m_capacity(0), m_size(0)
1228 m_capacity = Buf->Capacity();
1229 assert(m_p); assert(m_capacity);
1233 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
1235 if ( ( m_size + ber_len ) > m_capacity )
1238 if ( ! write_BER(m_p + m_size, i, ber_len) )
1246 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
1247 : m_p(0), m_capacity(0), m_size(0)
1249 m_p = Buf->RoData();
1250 m_capacity = Buf->Length();
1251 assert(m_p); assert(m_capacity);
1255 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
1257 if ( i == 0 || ber_len == 0 ) return false;
1259 if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
1262 if ( ( m_size + *ber_len ) > m_capacity )
1265 if ( ! read_BER(m_p + m_size, i) )
1272 //------------------------------------------------------------------------------------------
1274 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
1276 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
1281 Kumu::ByteString::~ByteString()
1288 // copy the given data into the ByteString, set Length value.
1289 // Returns error if the ByteString is too small.
1291 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
1293 if ( m_Capacity < buf_len )
1294 return RESULT_ALLOC;
1296 memcpy(m_Data, buf, buf_len);
1302 // copy the given data into the ByteString, set Length value.
1303 // Returns error if the ByteString is too small.
1305 Kumu::ByteString::Set(const ByteString& Buf)
1307 if ( m_Capacity < Buf.m_Capacity )
1308 return RESULT_ALLOC;
1310 memcpy(m_Data, Buf.m_Data, Buf.m_Length);
1311 m_Length = Buf.m_Length;
1316 // Sets the size of the internally allocate buffer.
1318 Kumu::ByteString::Capacity(ui32_t cap_size)
1320 if ( m_Capacity >= cap_size )
1323 byte_t* tmp_data = 0;
1332 if ( ( m_Data = (byte_t*)malloc(cap_size) ) == 0 )
1333 return RESULT_ALLOC;
1335 if ( tmp_data != 0 )
1337 assert(m_Length > 0);
1338 memcpy(m_Data, tmp_data, m_Length);
1342 m_Capacity = cap_size;
1348 Kumu::ByteString::Append(const ByteString& Buf)
1350 Result_t result = RESULT_OK;
1351 ui32_t diff = m_Capacity - m_Length;
1353 if ( diff < Buf.Length() )
1354 result = Capacity(m_Capacity + Buf.Length());
1356 if ( KM_SUCCESS(result) )
1358 memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
1359 m_Length += Buf.Length();
1367 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
1369 Result_t result = RESULT_OK;
1370 ui32_t diff = m_Capacity - m_Length;
1372 if ( diff < buf_len )
1373 result = Capacity(m_Capacity + buf_len);
1375 if ( KM_SUCCESS(result) )
1377 memcpy(m_Data + m_Length, buf, buf_len);
1378 m_Length += buf_len;