/* Copyright (c) 2005-2015, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file KM_util.cpp \version $Id$ \brief Utility functions */ #include #include #include #include #include #include #include #include #include #include const char* Kumu::Version() { return PACKAGE_VERSION; } //------------------------------------------------------------------------------------------ // Result_t Internals struct map_entry_t { int rcode; Kumu::Result_t* result; }; // WIN32 does not init this in time for use with Result_t(...) below, so it is // now a pointer that Result_t(...) fills in when it needs it. static Kumu::Mutex* s_MapLock = 0; static ui32_t s_MapSize = 0; static const ui32_t MapMax = 2048; static struct map_entry_t s_ResultMap[MapMax]; // const Kumu::Result_t& Kumu::Result_t::Find(int v) { if ( v == 0 ) return RESULT_OK; assert(s_MapLock); AutoMutex L(*s_MapLock); for ( ui32_t i = 0; i < s_MapSize; ++i ) { if ( s_ResultMap[i].rcode == v ) return *s_ResultMap[i].result; } return RESULT_UNKNOWN; } // Kumu::Result_t Kumu::Result_t::Delete(int v) { if ( v < -99 || v > 99 ) { DefaultLogSink().Error("Cannot delete core result code: %ld\n", v); return RESULT_FAIL; } assert(s_MapLock); AutoMutex L(*s_MapLock); for ( ui32_t i = 0; i < s_MapSize; ++i ) { if ( s_ResultMap[i].rcode == v ) { for ( ++i; i < s_MapSize; ++i ) s_ResultMap[i-1] = s_ResultMap[i]; --s_MapSize; return RESULT_OK; } } return RESULT_FALSE; } // unsigned int Kumu::Result_t::End() { return s_MapSize; } // const Kumu::Result_t& Kumu::Result_t::Get(unsigned int i) { return *s_ResultMap[i].result; } // Kumu::Result_t::Result_t(int v, const std::string& s, const std::string& l) : value(v), symbol(s), label(l) { assert(!l.empty()); assert(!s.empty()); if ( v == 0 ) return; // This may seem tricky, but it is certain that the static values declared in KM_error.h will // be created (and thus this method will be called) before main(...) is called. It is not // until then that threads could be created, thus the mutex will exist before multi-threaded // access could occur. if ( s_MapLock == 0 ) s_MapLock = new Kumu::Mutex; assert(s_MapLock); AutoMutex L(*s_MapLock); for ( ui32_t i = 0; i < s_MapSize; ++i ) { if ( s_ResultMap[i].rcode == v ) return; } assert(s_MapSize+1 < MapMax); s_ResultMap[s_MapSize].rcode = v; s_ResultMap[s_MapSize].result = this; ++s_MapSize; return; } Kumu::Result_t::Result_t(const Result_t& rhs) { value = rhs.value; symbol = rhs.symbol; label = rhs.label; message = rhs.message; } Kumu::Result_t::~Result_t() {} // const Kumu::Result_t& Kumu::Result_t::operator=(const Result_t& rhs) { value = rhs.value; symbol = rhs.symbol; label = rhs.label; message = rhs.message; return *this; } // const Kumu::Result_t Kumu::Result_t::operator()(const std::string& message) const { Result_t result = *this; result.message = message; return result; } static int const MESSAGE_BUF_MAX = 2048; // const Kumu::Result_t Kumu::Result_t::operator()(const int& line, const char* filename) const { assert(filename); char buf[MESSAGE_BUF_MAX]; snprintf(buf, MESSAGE_BUF_MAX-1, "%s, line %d", filename, line); Result_t result = *this; result.message = buf; return result; } // const Kumu::Result_t Kumu::Result_t::operator()(const std::string& message, const int& line, const char* filename) const { assert(filename); char buf[MESSAGE_BUF_MAX]; snprintf(buf, MESSAGE_BUF_MAX-1, "%s, line %d", filename, line); Result_t result = *this; result.message = message + buf; return result; } //------------------------------------------------------------------------------------------ // 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); } //------------------------------------------------------------------------------------------ const char fill = '='; const char* base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const byte_t decode_map[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; // Convert a binary string to NULL-terminated UTF-8 hexadecimal, returns the buffer // if the binary buffer was large enough to hold the result. If the output buffer // is too small or any of the pointer arguments are NULL, the subroutine will // return 0. // const char* Kumu::base64encode(const byte_t* buf, ui32_t buf_len, char* strbuf, ui32_t strbuf_len) { ui32_t out_char = 0; ui32_t i, block_len, diff; if ( buf == 0 || strbuf == 0 ) return 0; if ( strbuf_len < base64_encode_length(buf_len) + 1 ) return 0; block_len = buf_len; while ( block_len % 3 ) block_len--; for ( i = 0; i < block_len; i += 3 ) { strbuf[out_char++] = base64_chars[( buf[0] >> 2 )]; strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )]; strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) | ( buf[2] >> 6 ) )]; strbuf[out_char++] = base64_chars[( buf[2] & 0x3f )]; buf += 3; } if ( i < buf_len ) { diff = buf_len - i; assert(diff > 0); assert(diff < 3); strbuf[out_char++] = base64_chars[( buf[0] >> 2 )]; if ( diff == 1 ) { strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) )]; strbuf[out_char++] = fill; } else if ( diff == 2 ) { strbuf[out_char++] = base64_chars[( ( ( buf[0] & 0x03 ) << 4 ) | ( buf[1] >> 4 ) )]; strbuf[out_char++] = base64_chars[( ( ( buf[1] & 0x0f ) << 2 ) )]; } strbuf[out_char++] = fill; } strbuf[out_char] = 0; return strbuf;; } // Convert NULL-terminated UTF-8 Base64 string to binary, returns 0 if // the binary buffer was large enough to hold the result. The output parameter // 'char_count' will contain the length of the converted string. If the output // buffer is too small or any of the pointer arguments are NULL, the subroutine // will return -1 and set 'char_count' to the required buffer size. No data will // be written to 'buf' if the subroutine fails. // i32_t Kumu::base64decode(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* char_count) { register byte_t c = 0, d = 0; register ui32_t phase = 0, i = 0; if ( str == 0 || buf == 0 || char_count == 0 ) return -1; while ( *str != 0 && i < buf_len ) { c = decode_map[(int)*str++]; if ( c == 0xff ) continue; if ( c == 0xfe ) break; switch ( phase++ ) { case 0: buf[i++] = c << 2; break; case 1: buf[i - 1] |= c >> 4; d = c; break; case 2: buf[i++] = ( d << 4 ) | ( c >> 2 ); d = c; break; case 3: buf[i++] = ( d << 6 ) | c; phase = 0; break; } } *char_count = i; return 0; } //------------------------------------------------------------------------------------------ // convert utf-8 hext string to bin i32_t Kumu::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size) { KM_TEST_NULL_L(str); KM_TEST_NULL_L(buf); KM_TEST_NULL_L(conv_size); *conv_size = 0; if ( str[0] == 0 ) // nothing to convert return 0; for ( int j = 0; str[j]; j++ ) { if ( isxdigit(str[j]) ) (*conv_size)++; } if ( *conv_size & 0x01 ) (*conv_size)++; *conv_size /= 2; if ( *conv_size > buf_len )// maximum possible data size return -1; *conv_size = 0; int phase = 0; // track high/low nybble // for each character, fill in the high nybble then the low for ( int i = 0; str[i]; i++ ) { if ( ! isxdigit(str[i]) ) continue; byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) ); if ( phase == 0 ) { buf[*conv_size] = val << 4; phase++; } else { buf[*conv_size] |= val; phase = 0; (*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 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 // const char* Kumu::bin2hex(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; #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++ ) { *p = (bin_buf[i] >> 4) & 0x0f; *p += *p < 10 ? 0x30 : 0x61 - 10; p++; *p = bin_buf[i] & 0x0f; *p += *p < 10 ? 0x30 : 0x61 - 10; p++; } *p = '\0'; return str_buf; } // spew a range of bin data as hex void Kumu::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream) { if ( buf == 0 ) return; if ( stream == 0 ) stream = stderr; static ui32_t row_len = 16; const byte_t* p = buf; const byte_t* end_p = p + dump_len; for ( ui32_t line = 0; p < end_p; line++ ) { fprintf(stream, " %06x: ", line); ui32_t i; const byte_t* pp; for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ ) fprintf(stream, "%02x ", *pp); while ( i++ < row_len ) fputs(" ", stream); for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ ) fputc((isprint(*pp) ? *pp : '.'), stream); fputc('\n', stream); p += row_len; } } // const char* Kumu::bin2UUIDhex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len) { ui32_t i, j, k; if ( str_len < 34 || bin_len != UUID_Length ) return 0; if ( bin2hex(bin_buf, bin_len, str_buf, str_len) == 0 ) return 0; // shift the node id for ( k = 19, i = 12; i > 0; i-- ) str_buf[k+i+4] = str_buf[k+i]; // shift the time (mid+hi+clk) for ( k = 15, j = 3; k > 6; k -= 4, j-- ) { for ( i = 4; i > 0; i-- ) str_buf[k+i+j] = str_buf[k+i]; } // add in the hyphens and trainling null for ( i = 8; i < 24; i += 5 ) str_buf[i] = '-'; str_buf[36] = 0; return str_buf; } // void Kumu::GenRandomValue(UUID& ID) { byte_t tmp_buf[UUID_Length]; GenRandomUUID(tmp_buf); ID.Set(tmp_buf); } // void Kumu::GenRandomUUID(byte_t* buf) { FortunaRNG RNG; RNG.FillRandom(buf, UUID_Length); buf[6] &= 0x0f; // clear bits 4-7 buf[6] |= 0x40; // set UUID version buf[8] &= 0x3f; // clear bits 6&7 buf[8] |= 0x80; // set bit 7 } // void Kumu::GenRandomValue(SymmetricKey& Key) { byte_t tmp_buf[SymmetricKey_Length]; FortunaRNG RNG; RNG.FillRandom(tmp_buf, SymmetricKey_Length); Key.Set(tmp_buf); } //------------------------------------------------------------------------------------------ // read a ber value from the buffer and compare with test value. // Advances buffer to first character after BER value. // bool Kumu::read_test_BER(byte_t **buf, ui64_t test_value) { if ( buf == 0 ) return false; if ( ( **buf & 0x80 ) == 0 ) return false; ui64_t val = 0; ui8_t ber_size = ( **buf & 0x0f ) + 1; if ( ber_size > 9 ) return false; for ( ui8_t i = 1; i < ber_size; i++ ) { if ( (*buf)[i] > 0 ) val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 ); } *buf += ber_size; return ( val == test_value ); } // bool Kumu::read_BER(const byte_t* buf, ui64_t* val) { ui8_t ber_size, i; if ( buf == 0 || val == 0 ) return false; if ( ( *buf & 0x80 ) == 0 ) return false; *val = 0; ber_size = ( *buf & 0x0f ) + 1; if ( ber_size > 9 ) return false; for ( i = 1; i < ber_size; i++ ) { if ( buf[i] > 0 ) *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 ); } return true; } static const ui64_t ber_masks[9] = { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000), ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000), ui64_C(0xffff000000000000), ui64_C(0xff00000000000000), 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 Kumu::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len) { if ( buf == 0 ) return false; if ( ber_len == 0 ) { // calculate default length if ( val < 0x01000000L ) ber_len = 4; else if ( val < ui64_C(0x0100000000000000) ) ber_len = 8; else ber_len = 9; } else { // sanity check BER length if ( ber_len > 9 ) { DefaultLogSink().Error("BER integer length %u exceeds maximum size of 9\n", ber_len); return false; } if ( ( val & ber_masks[ber_len - 1] ) != 0 ) { ui64Printer tmp_i(val); DefaultLogSink().Error("BER integer length %u too small for value %s\n", ber_len, tmp_i.c_str()); return false; } } buf[0] = 0x80 + ( ber_len - 1 ); for ( ui32_t i = ber_len - 1; i > 0; i-- ) { buf[i] = (ui8_t)(val & 0xff); val >>= 8; } return true; } //------------------------------------------------------------------------------------------ #ifndef KM_WIN32 #include #endif // Kumu::Timestamp::Timestamp() : m_TZOffsetMinutes(0) { m_Timestamp.now(); } Kumu::Timestamp::Timestamp(const Timestamp& rhs) { m_Timestamp = rhs.m_Timestamp; m_TZOffsetMinutes = rhs.m_TZOffsetMinutes; } Kumu::Timestamp::Timestamp(const char* datestr) : m_TZOffsetMinutes(0) { DecodeString(datestr); } Kumu::Timestamp::~Timestamp() { } // const Kumu::Timestamp& Kumu::Timestamp::operator=(const Timestamp& rhs) { m_Timestamp = rhs.m_Timestamp; m_TZOffsetMinutes = rhs.m_TZOffsetMinutes; return *this; } bool Kumu::Timestamp::operator<(const Timestamp& rhs) const { return m_Timestamp.x < rhs.m_Timestamp.x; } bool Kumu::Timestamp::operator>(const Timestamp& rhs) const { return m_Timestamp.x > rhs.m_Timestamp.x; } bool Kumu::Timestamp::operator==(const Timestamp& rhs) const { return m_Timestamp.x == rhs.m_Timestamp.x; } bool Kumu::Timestamp::operator!=(const Timestamp& rhs) const { return m_Timestamp.x != rhs.m_Timestamp.x; } // void Kumu::Timestamp::GetComponents(ui16_t& Year, ui8_t& Month, ui8_t& Day, ui8_t& Hour, ui8_t& Minute, ui8_t& Second) const { TAI::caltime ct; ct = m_Timestamp; Year = ct.date.year; Month = ct.date.month; Day = ct.date.day; Hour = ct.hour; Minute = ct.minute; Second = ct.second; } // void Kumu::Timestamp::SetComponents(const ui16_t& Year, const ui8_t& Month, const ui8_t& Day, const ui8_t& Hour, const ui8_t& Minute, const ui8_t& Second) { TAI::caltime ct; ct.date.year = Year; ct.date.month = Month; ct.date.day = Day; ct.hour = Hour; ct.minute = Minute; ct.second = Second; ct.offset = 0; m_Timestamp = ct; m_TZOffsetMinutes = 0; } // returns false if the requested adjustment is out of range bool Kumu::Timestamp::SetTZOffsetMinutes(const i32_t& minutes) { static const i32_t tz_limit = 14 * 60 * 60; if ( minutes < ( - tz_limit) || minutes > tz_limit ) return false; m_TZOffsetMinutes = minutes; return true; } // const char* Kumu::Timestamp::EncodeString(char* str_buf, ui32_t buf_len) const { if ( buf_len < ( DateTimeLen + 1 ) ) return 0; ui16_t year; ui8_t month, day, hour, minute, second; ui32_t ofst_hours = 0, ofst_minutes = 0; char direction = '+'; if ( m_TZOffsetMinutes == 0 ) { GetComponents(year, month, day, hour, minute, second); } else { // calculate local time Kumu::Timestamp tmp_t(*this); tmp_t.AddMinutes(m_TZOffsetMinutes); tmp_t.GetComponents(year, month, day, hour, minute, second); ofst_hours = Kumu::xabs(m_TZOffsetMinutes) / 60; ofst_minutes = Kumu::xabs(m_TZOffsetMinutes) % 60; if ( m_TZOffsetMinutes < 0 ) direction = '-'; } // 2004-05-01T13:20:00+00:00 snprintf(str_buf, buf_len, "%04hu-%02hhu-%02hhuT%02hhu:%02hhu:%02hhu%c%02u:%02u", year, month, day, hour, minute, second, direction, ofst_hours, ofst_minutes); return str_buf; } // ^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(?:([+-]\d{2}):(\d{2}))?)?$ bool Kumu::Timestamp::DecodeString(const char* datestr) { if ( ! ( isdigit(datestr[0]) && isdigit(datestr[1]) && isdigit(datestr[2]) && isdigit(datestr[3]) ) || datestr[4] != '-' || ! ( isdigit(datestr[5]) && isdigit(datestr[6]) ) || datestr[7] != '-' || ! ( isdigit(datestr[8]) && isdigit(datestr[9]) ) ) return false; ui32_t char_count = 10; TAI::caltime YMDhms; YMDhms.hour = 0; YMDhms.minute = 0; YMDhms.second = 0; YMDhms.offset = 0; YMDhms.date.year = strtol(datestr, 0, 10); YMDhms.date.month = strtol(datestr + 5, 0, 10); YMDhms.date.day = strtol(datestr + 8, 0, 10); if ( datestr[10] == 'T' ) { if ( ! ( isdigit(datestr[11]) && isdigit(datestr[12]) ) || datestr[13] != ':' || ! ( isdigit(datestr[14]) && isdigit(datestr[15]) ) ) return false; char_count += 6; YMDhms.hour = strtol(datestr + 11, 0, 10); YMDhms.minute = strtol(datestr + 14, 0, 10); if ( datestr[16] == ':' ) { if ( ! ( isdigit(datestr[17]) && isdigit(datestr[18]) ) ) return false; char_count += 3; YMDhms.second = strtol(datestr + 17, 0, 10); } if ( datestr[19] == '.' ) { if ( ! isdigit(datestr[20]) ) { return false; } // we don't carry the ms value while ( isdigit(datestr[20]) ) { ++datestr; } ++datestr; } if ( datestr[19] == '-' || datestr[19] == '+' ) { if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) ) || datestr[22] != ':' || ! ( isdigit(datestr[23]) && isdigit(datestr[24]) ) ) return false; char_count += 6; ui32_t TZ_hh = strtol(datestr + 20, 0, 10); ui32_t TZ_mm = strtol(datestr + 23, 0, 10); 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 */ YMDhms.offset = TZ_offset; } else if (datestr[19] == 'Z') { /* act as if the offset were +00:00 */ char_count++; } } if ( datestr[char_count] != 0 ) { Kumu::DefaultLogSink().Error("Unexpected extra characters in string: %s (%ld)\n", datestr, char_count); return false; } m_Timestamp = YMDhms; m_TZOffsetMinutes = YMDhms.offset; return true; } // bool Kumu::Timestamp::HasValue() const { return true; } // bool Kumu::Timestamp::Unarchive(MemIOReader* Reader) { ui16_t year; ui8_t month, day, hour, minute, second, tick; assert(Reader); if ( ! Reader->ReadUi16BE(&year) ) return false; if ( ! Reader->ReadUi8(&month) ) return false; if ( ! Reader->ReadUi8(&day) ) return false; if ( ! Reader->ReadUi8(&hour) ) return false; if ( ! Reader->ReadUi8(&minute) ) return false; if ( ! Reader->ReadUi8(&second) ) return false; if ( ! Reader->ReadUi8(&tick) ) return false; SetComponents(year, month, day, hour, minute, second); return true; } // bool Kumu::Timestamp::Archive(MemIOWriter* Writer) const { assert(Writer); ui16_t year; ui8_t month, day, hour, minute, second, tick = 0; GetComponents(year, month, day, hour, minute, second); if ( ! Writer->WriteUi16BE(year) ) return false; if ( ! Writer->WriteUi8(month) ) return false; if ( ! Writer->WriteUi8(day) ) return false; if ( ! Writer->WriteUi8(hour) ) return false; if ( ! Writer->WriteUi8(minute) ) return false; if ( ! Writer->WriteUi8(second) ) return false; if ( ! Writer->WriteUi8(tick) ) return false; return true; } // ui64_t Kumu::Timestamp::GetCTime() const { return m_Timestamp.x - ui64_C(4611686018427387914); } // void Kumu::Timestamp::SetCTime(const ui64_t& ctime) { m_Timestamp.x = ctime + ui64_C(4611686018427387914); } //------------------------------------------------------------------------------------------ Kumu::MemIOWriter::MemIOWriter(ByteString* Buf) : m_p(0), m_capacity(0), m_size(0) { m_p = Buf->Data(); m_capacity = Buf->Capacity(); assert(m_p); assert(m_capacity); } bool Kumu::MemIOWriter:: WriteBER(ui64_t i, ui32_t ber_len) { if ( ( m_size + ber_len ) > m_capacity ) return false; if ( ! write_BER(m_p + m_size, i, ber_len) ) return false; m_size += ber_len; return true; } Kumu::MemIOReader::MemIOReader(const ByteString* Buf) : m_p(0), m_capacity(0), m_size(0) { m_p = Buf->RoData(); m_capacity = Buf->Length(); assert(m_p); assert(m_capacity); } bool Kumu::MemIOReader::ReadBER(ui64_t* i, ui32_t* ber_len) { if ( i == 0 || ber_len == 0 ) return false; if ( ( *ber_len = BER_length(m_p + m_size) ) == 0 ) return false; if ( ( m_size + *ber_len ) > m_capacity ) return false; if ( ! read_BER(m_p + m_size, i) ) return false; m_size += *ber_len; return true; } //------------------------------------------------------------------------------------------ Kumu::ByteString::ByteString() : m_Data(0), m_Capacity(0), m_Length(0) {} Kumu::ByteString::ByteString(ui32_t cap) : m_Data(0), m_Capacity(0), m_Length(0) { Capacity(cap); } Kumu::ByteString::~ByteString() { if ( m_Data != 0 ) free(m_Data); } // 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 byte_t* buf, ui32_t buf_len) { if ( m_Capacity < buf_len ) return RESULT_ALLOC; memcpy(m_Data, buf, buf_len); m_Length = buf_len; return RESULT_OK; } // 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. Kumu::Result_t Kumu::ByteString::Capacity(ui32_t cap_size) { if ( m_Capacity >= cap_size ) return RESULT_OK; byte_t* tmp_data = 0; if ( m_Data != 0 ) { if ( m_Length > 0 ) tmp_data = m_Data; else free(m_Data); } 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; } // Kumu::Result_t Kumu::ByteString::Append(const ByteString& Buf) { Result_t result = RESULT_OK; ui32_t diff = m_Capacity - m_Length; if ( diff < Buf.Length() ) result = Capacity(m_Capacity + Buf.Length()); if ( KM_SUCCESS(result) ) { memcpy(m_Data + m_Length, Buf.RoData(), Buf.Length()); m_Length += Buf.Length(); } return result; } // Kumu::Result_t Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len) { Result_t result = RESULT_OK; ui32_t diff = m_Capacity - m_Length; if ( diff < buf_len ) result = Capacity(m_Capacity + buf_len); if ( KM_SUCCESS(result) ) { memcpy(m_Data + m_Length, buf, buf_len); m_Length += buf_len; } return result; } //------------------------------------------------------------------------------------------ // const char* Kumu::km_strnstr(const char *s, const char *find, size_t slen) { char c, sc; size_t len; if ( ( c = *find++ ) != '\0' ) { len = strlen(find); do { do { if ( slen-- < 1 || ( sc = *s++ ) == '\0' ) return 0; } while ( sc != c ); if ( len > slen ) return 0; } while ( strncmp(s, find, len) != 0 ); --s; } return s; } // std::list Kumu::km_token_split(const std::string& str, const std::string& separator) { std::list components; const char* pstr = str.c_str(); const char* r = strstr(pstr, separator.c_str()); while ( r != 0 ) { assert(r >= pstr); std::string tmp_str; tmp_str.assign(pstr, r - pstr); components.push_back(tmp_str); pstr = r + separator.size(); r = strstr(pstr, separator.c_str()); } components.push_back(std::string(pstr)); return components; } // // end KM_util.cpp //