2 Copyright (c) 2005-2006, 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>
41 //------------------------------------------------------------------------------------------
44 const char fill = '=';
45 const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
47 const byte_t decode_map[] =
48 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
49 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
50 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
51 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
52 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
53 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63,
54 52, 53, 54, 55, 56, 57, 58, 59,
55 60, 61, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
56 0xff, 0, 1, 2, 3, 4, 5, 6,
57 7, 8, 9, 10, 11, 12, 13, 14,
58 15, 16, 17, 18, 19, 20, 21, 22,
59 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff,
60 0xff, 26, 27, 28, 29, 30, 31, 32,
61 33, 34, 35, 36, 37, 38, 39, 40,
62 41, 42, 43, 44, 45, 46, 47, 48,
63 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff,
64 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
65 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
66 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
67 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
68 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
69 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
70 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
71 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
72 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
73 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
74 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
75 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
76 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
77 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
78 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
79 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
83 // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer
84 // if the binary buffer was large enough to hold the result. If the output buffer
85 // is too small or any of the pointer arguments are NULL, the subroutine will
89 Kumu::base64encode(const byte_t* buf, ui32_t buf_len, char* strbuf, ui32_t strbuf_len)
92 ui32_t i, block_len, diff;
94 if ( buf == 0 || strbuf == 0 )
97 if ( strbuf_len < base64_encode_length(buf_len) + 1 )
102 while ( block_len % 3 )
105 for ( i = 0; i < block_len; i += 3 )
107 strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
108 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
109 strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) | ( buf[2] >> 6 ) )];
110 strbuf[out_char++] = base64_chars[( buf[2] & 0x3f )];
120 strbuf[out_char++] = base64_chars[( buf[0] >> 2 )];
124 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) )];
125 strbuf[out_char++] = fill;
127 else if ( diff == 2 )
129 strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )];
130 strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) )];
133 strbuf[out_char++] = fill;
136 strbuf[out_char] = 0;
143 // Convert NULL-terminated UTF-8 Base64 string to binary, returns 0 if
144 // the binary buffer was large enough to hold the result. The output parameter
145 // 'char_count' will contain the length of the converted string. If the output
146 // buffer is too small or any of the pointer arguments are NULL, the subroutine
147 // will return -1 and set 'char_count' to the required buffer size. No data will
148 // be written to 'buf' if the subroutine fails.
151 Kumu::base64decode(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count)
153 register byte_t c = 0, d = 0;
154 register ui32_t phase = 0, i = 0;
156 if ( str == 0 || buf == 0 || char_count == 0 )
159 while ( *str != 0 && i < buf_len )
161 c = decode_map[(int)*str++];
162 if ( c == 0xff ) continue;
163 if ( c == 0xfe ) break;
172 buf[i - 1] |= c >> 4;
177 buf[i++] = ( d << 4 ) | ( c >> 2 );
182 buf[i++] = ( d << 6 ) | c;
192 //------------------------------------------------------------------------------------------
194 // convert utf-8 hext string to bin
196 Kumu::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size)
200 KM_TEST_NULL(conv_size);
204 if ( str[0] == 0 ) // nothing to convert
207 for ( int j = 0; str[j]; j++ )
209 if ( isxdigit(str[j]) )
213 if ( *conv_size & 0x01 ) (*conv_size)++;
216 if ( *conv_size > buf_len )// maximum possible data size
221 int phase = 0; // track high/low nybble
223 // for each character, fill in the high nybble then the low
224 for ( int i = 0; str[i]; i++ )
226 if ( ! isxdigit(str[i]) )
229 byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) );
233 buf[*conv_size] = val << 4;
238 buf[*conv_size] |= val;
248 // convert a memory region to a NULL-terminated hexadecimal string
251 Kumu::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
255 || ((bin_len * 2) + 1) > str_len )
260 for ( ui32_t i = 0; i < bin_len; i++ )
262 *p = (bin_buf[i] >> 4) & 0x0f;
263 *p += *p < 10 ? 0x30 : 0x61 - 10;
266 *p = bin_buf[i] & 0x0f;
267 *p += *p < 10 ? 0x30 : 0x61 - 10;
276 // spew a range of bin data as hex
278 Kumu::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream)
286 static ui32_t row_len = 16;
287 const byte_t* p = buf;
288 const byte_t* end_p = p + dump_len;
290 for ( ui32_t line = 0; p < end_p; line++ )
292 fprintf(stream, " %06x: ", line);
296 for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
297 fprintf(stream, "%02x ", *pp);
299 while ( i++ < row_len )
302 for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
303 fputc((isprint(*pp) ? *pp : '.'), stream);
312 Kumu::bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
316 if ( str_len < 34 || bin_len != UUID_Length )
319 if ( bin2hex(bin_buf, bin_len, str_buf, str_len) == 0 )
323 for ( k = 19, i = 12; i > 0; i-- )
324 str_buf[k+i+4] = str_buf[k+i];
326 // shift the time (mid+hi+clk)
327 for ( k = 15, j = 3; k > 6; k -= 4, j-- )
329 for ( i = 4; i > 0; i-- )
330 str_buf[k+i+j] = str_buf[k+i];
333 // add in the hyphens and trainling null
334 for ( i = 8; i < 24; i += 5 )
343 Kumu::GenRandomValue(UUID& ID)
345 byte_t tmp_buf[UUID_Length];
346 GenRandomUUID(tmp_buf);
352 Kumu::GenRandomUUID(byte_t* buf)
355 RNG.FillRandom(buf, UUID_Length);
356 buf[6] &= 0x0f; // clear bits 4-7
357 buf[6] |= 0x40; // set UUID version
358 buf[8] &= 0x3f; // clear bits 6&7
359 buf[8] |= 0x80; // set bit 7
364 Kumu::GenRandomValue(SymmetricKey& ID)
366 byte_t tmp_buf[SymmetricKey_Length];
368 RNG.FillRandom(tmp_buf, SymmetricKey_Length);
373 //------------------------------------------------------------------------------------------
374 // read a ber value from the buffer and compare with test value.
375 // Advances buffer to first character after BER value.
377 // read a ber value from the buffer and compare with test value.
378 // Advances buffer to first character after BER value.
381 Kumu::read_test_BER(byte_t **buf, ui64_t test_value)
386 if ( ( **buf & 0x80 ) == 0 )
390 ui8_t ber_size = ( **buf & 0x0f ) + 1;
395 for ( ui8_t i = 1; i < ber_size; i++ )
398 val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
402 return ( val == test_value );
408 Kumu::read_BER(const byte_t* buf, ui64_t* val)
412 if ( buf == 0 || val == 0 )
415 if ( ( *buf & 0x80 ) == 0 )
419 ber_size = ( *buf & 0x0f ) + 1;
424 for ( i = 1; i < ber_size; i++ )
427 *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
434 static const ui64_t ber_masks[9] =
435 { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00),
436 ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
437 ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
438 ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
445 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
451 { // calculate default length
452 if ( val < 0x01000000L )
454 else if ( val < ui64_C(0x0100000000000000) )
460 { // sanity check BER length
463 DefaultLogSink().Error("BER size %lu exceeds maximum size of 9\n", ber_len);
467 if ( val & ber_masks[ber_len - 1] )
469 ui64Printer tmp_i(val);
470 DefaultLogSink().Error("BER size %lu too small for value %s\n", tmp_i.c_str());
475 buf[0] = 0x80 + ( ber_len - 1 );
477 for ( ui32_t i = ber_len - 1; i > 0; i-- )
479 buf[i] = (ui8_t)(val & 0xff);
487 //------------------------------------------------------------------------------------------
490 #define TIMESTAMP_TO_SYSTIME(ts, t) \
491 (t)->wYear = (ts).Year; /* year */ \
492 (t)->wMonth = (ts).Month; /* month of year (1 - 12) */ \
493 (t)->wDay = (ts).Day; /* day of month (1 - 31) */ \
494 (t)->wHour = (ts).Hour; /* hours (0 - 23) */ \
495 (t)->wMinute = (ts).Minute; /* minutes (0 - 59) */ \
496 (t)->wSecond = (ts).Second; /* seconds (0 - 60) */ \
497 (t)->wDayOfWeek = 0; \
498 (t)->wMilliseconds = 0
500 #define SYSTIME_TO_TIMESTAMP(t, ts) \
501 (ts).Year = (t)->wYear; /* year */ \
502 (ts).Month = (t)->wMonth; /* month of year (1 - 12) */ \
503 (ts).Day = (t)->wDay; /* day of month (1 - 31) */ \
504 (ts).Hour = (t)->wHour; /* hours (0 - 23) */ \
505 (ts).Minute = (t)->wMinute; /* minutes (0 - 59) */ \
506 (ts).Second = (t)->wSecond; /* seconds (0 - 60) */
509 Kumu::Timestamp::Timestamp() :
510 Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
513 GetSystemTime(&sys_time);
514 SYSTIME_TO_TIMESTAMP(&sys_time, *this);
519 Kumu::Timestamp::operator<(const Timestamp& rhs) const
521 SYSTEMTIME lhst, rhst;
524 TIMESTAMP_TO_SYSTIME(*this, &lhst);
525 TIMESTAMP_TO_SYSTIME(rhs, &rhst);
526 SystemTimeToFileTime(&lhst, &lft);
527 SystemTimeToFileTime(&rhst, &rft);
528 return ( CompareFileTime(&lft, &rft) == -1 );
532 seconds_to_ns100(ui32_t seconds)
534 return ((ui64_t)seconds * 10000000);
539 Kumu::Timestamp::AddDays(i32_t days)
541 SYSTEMTIME current_st;
543 ULARGE_INTEGER current_ul;
547 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
548 SystemTimeToFileTime(¤t_st, ¤t_ft);
549 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
550 current_ul.QuadPart += ( seconds_to_ns100(86400) * (ui64_t)days );
551 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
552 FileTimeToSystemTime(¤t_ft, ¤t_st);
553 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
559 Kumu::Timestamp::AddHours(i32_t hours)
561 SYSTEMTIME current_st;
563 ULARGE_INTEGER current_ul;
567 TIMESTAMP_TO_SYSTIME(*this, ¤t_st);
568 SystemTimeToFileTime(¤t_st, ¤t_ft);
569 memcpy(¤t_ul, ¤t_ft, sizeof(current_ul));
570 current_ul.QuadPart += ( seconds_to_ns100(3600) * (ui64_t)hours );
571 memcpy(¤t_ft, ¤t_ul, sizeof(current_ft));
572 FileTimeToSystemTime(¤t_ft, ¤t_st);
573 SYSTIME_TO_TIMESTAMP(¤t_st, *this);
581 #define TIMESTAMP_TO_TM(ts, t) \
582 (t)->tm_year = (ts).Year - 1900; /* year - 1900 */ \
583 (t)->tm_mon = (ts).Month - 1; /* month of year (0 - 11) */ \
584 (t)->tm_mday = (ts).Day; /* day of month (1 - 31) */ \
585 (t)->tm_hour = (ts).Hour; /* hours (0 - 23) */ \
586 (t)->tm_min = (ts).Minute; /* minutes (0 - 59) */ \
587 (t)->tm_sec = (ts).Second; /* seconds (0 - 60) */
589 #define TM_TO_TIMESTAMP(t, ts) \
590 (ts).Year = (t)->tm_year + 1900; /* year - 1900 */ \
591 (ts).Month = (t)->tm_mon + 1; /* month of year (0 - 11) */ \
592 (ts).Day = (t)->tm_mday; /* day of month (1 - 31) */ \
593 (ts).Hour = (t)->tm_hour; /* hours (0 - 23) */ \
594 (ts).Minute = (t)->tm_min; /* minutes (0 - 59) */ \
595 (ts).Second = (t)->tm_sec; /* seconds (0 - 60) */
598 Kumu::Timestamp::Timestamp() :
599 Year(0), Month(0), Day(0), Hour(0), Minute(0), Second(0)
601 time_t t_now = time(0);
602 struct tm* now = gmtime(&t_now);
603 TM_TO_TIMESTAMP(now, *this);
608 Kumu::Timestamp::operator<(const Timestamp& rhs) const
610 struct tm lhtm, rhtm;
611 TIMESTAMP_TO_TM(*this, &lhtm);
612 TIMESTAMP_TO_TM(rhs, &rhtm);
613 return ( timegm(&lhtm) < timegm(&rhtm) );
618 Kumu::Timestamp::AddDays(i32_t days)
624 TIMESTAMP_TO_TM(*this, ¤t);
625 time_t adj_time = timegm(¤t);
626 adj_time += 86400 * days;
627 struct tm* now = gmtime(&adj_time);
628 TM_TO_TIMESTAMP(now, *this);
634 Kumu::Timestamp::AddHours(i32_t hours)
640 TIMESTAMP_TO_TM(*this, ¤t);
641 time_t adj_time = timegm(¤t);
642 adj_time += 3600 * hours;
643 struct tm* now = gmtime(&adj_time);
644 TM_TO_TIMESTAMP(now, *this);
651 Kumu::Timestamp::Timestamp(const Timestamp& rhs)
661 Kumu::Timestamp::~Timestamp()
666 const Kumu::Timestamp&
667 Kumu::Timestamp::operator=(const Timestamp& rhs)
680 Kumu::Timestamp::operator==(const Timestamp& rhs) const
682 if ( Year == rhs.Year
683 && Month == rhs.Month
686 && Minute == rhs.Minute
687 && Second == rhs.Second )
695 Kumu::Timestamp::operator!=(const Timestamp& rhs) const
697 if ( Year != rhs.Year
698 || Month != rhs.Month
701 || Minute != rhs.Minute
702 || Second != rhs.Second )
710 Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const
712 if ( buf_len < ( DateTimeLen + 1 ) )
715 // 2004-05-01T13:20:00-00:00
716 snprintf(str_buf, buf_len,
717 "%04hu-%02hu-%02huT%02hu:%02hu:%02hu-00:00",
718 Year, Month, Day, Hour, Minute, Second);
725 Kumu::Timestamp::HasValue() const
727 if ( Year || Month || Day || Hour || Minute || Second )
735 Kumu::Timestamp::Unarchive(MemIOReader* Reader)
738 if ( ! Reader->ReadUi16BE(&Year) ) return false;
739 if ( ! Reader->ReadRaw(&Month, 6) ) return false;
745 Kumu::Timestamp::Archive(MemIOWriter* Writer) const
748 if ( ! Writer->WriteUi16BE(Year) ) return false;
749 if ( ! Writer->WriteRaw(&Month, 6) ) return false;
756 Kumu::UnarchiveString(MemIOReader* Reader, std::string&)
760 if ( ! Reader->ReadUi32BE(&str_length) ) return false;
761 assign((const char*)Reader->CurrentData(), str_length);
762 if ( ! Reader->SkipOffset(str_length) ) return false;
768 Kumu::String::Archive(MemIOWriter* Writer) const
771 if ( ! Writer->WriteUi32BE(length()) ) return false;
772 if ( ! Writer->WriteRaw((const byte_t*)c_str(), length()) ) return false;
778 //------------------------------------------------------------------------------------------
780 Kumu::MemIOWriter::MemIOWriter(ByteString* Buf)
781 : m_p(0), m_capacity(0), m_size(0)
784 m_capacity = Buf->Capacity();
785 assert(m_p); assert(m_capacity);
789 Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len)
791 if ( ( m_size + ber_len ) > m_capacity )
794 if ( ! write_BER(m_p + m_size, i, ber_len) )
802 Kumu::MemIOReader::MemIOReader(const ByteString* Buf)
803 : m_p(0), m_capacity(0), m_size(0)
806 m_capacity = Buf->Capacity();
807 assert(m_p); assert(m_capacity);
811 Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len)
813 if ( i == 0 || ber_len == 0 ) return false;
815 if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 )
818 if ( ( m_size + *ber_len ) > m_capacity )
821 if ( ! read_BER(m_p + m_size, i) )
828 //------------------------------------------------------------------------------------------
830 Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {}
832 Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0)
837 Kumu::ByteString::~ByteString()
844 // copy the given data into the ByteString, set Length value.
845 // Returns error if the ByteString is too small.
847 Kumu::ByteString::Set(const byte_t* buf, ui32_t buf_len)
849 if ( m_Capacity < buf_len )
852 memcpy(m_Data, buf, buf_len);
858 // Sets the size of the internally allocate buffer.
859 // Resets content length to zero.
861 Kumu::ByteString::Capacity(ui32_t cap_size)
863 if ( m_Capacity < cap_size )
868 m_Data = (byte_t*)malloc(cap_size);
873 m_Capacity = cap_size;
882 Kumu::ByteString::Append(const ByteString& Buf)
884 Result_t result = RESULT_OK;
885 ui32_t diff = m_Capacity - m_Length;
887 if ( diff < Buf.Length() )
888 result = Capacity(m_Capacity + Buf.Length());
890 if ( KM_SUCCESS(result) )
892 memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length());
893 m_Length += Buf.Length();
901 Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
903 Result_t result = RESULT_OK;
904 ui32_t diff = m_Capacity - m_Length;
906 if ( diff < buf_len )
907 result = Capacity(m_Capacity + buf_len);
909 if ( KM_SUCCESS(result) )
911 memcpy(m_Data + m_Length, buf, buf_len);