blob: 878ea2adfa76504f8f9428f8afc1db90f5924925 [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file contains implementation of the TimeZoneUtcOffset class used in Time Services
* WEAVE_CONFIG_TIME must be defined if Time Services are needed
*
*/
// __STDC_LIMIT_MACROS must be defined for UINT8_MAX to be defined for pre-C++11 clib
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif // __STDC_LIMIT_MACROS
// __STDC_CONSTANT_MACROS must be defined for INT64_C and UINT64_C to be defined for pre-C++11 clib
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif // __STDC_CONSTANT_MACROS
// it is important for this first inclusion of stdint.h to have all the right switches turned ON
#include <stdint.h>
// __STDC_FORMAT_MACROS must be defined for PRIX64 to be defined for pre-C++11 clib
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif // __STDC_FORMAT_MACROS
// it is important for this first inclusion of inttypes.h to have all the right switches turned ON
#include <inttypes.h>
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Profiles/time/WeaveTime.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/ErrorStr.h>
#include <Weave/Support/MathUtils.h>
#include <Weave/Support/logging/WeaveLogging.h>
#if WEAVE_CONFIG_TIME
using namespace nl::Weave::Profiles::Time;
WEAVE_ERROR TimeZoneUtcOffset::GetCurrentLocalTime(timesync_t * const aLocalTime, const timesync_t aUtcTime) const
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
bool found = false;
if (1 == mSize)
{
// ignore the begin time check, as it shall be zero through decoding, anyway
found = true;
*aLocalTime = aUtcTime + timesync_t(mUtcOffsetRecord[0].mUtcOffset_sec) * 1000000;
}
else if (mSize >= 2)
{
for (int i = 0; i < mSize - 1; ++i)
{
if ((aUtcTime >= mUtcOffsetRecord[i].mBeginAt_usec) && (aUtcTime < mUtcOffsetRecord[i + 1].mBeginAt_usec))
{
// we found it!
found = true;
*aLocalTime = aUtcTime + timesync_t(mUtcOffsetRecord[i].mUtcOffset_sec) * 1000000;
break;
}
}
}
else
{
// we don't have any records !
}
if (!found)
{
ExitNow(err= WEAVE_ERROR_KEY_NOT_FOUND);
}
exit:
WeaveLogFunctError(err);
return err;
}
WEAVE_ERROR TimeZoneUtcOffset::Decode(const uint8_t * const aInputBuf, const uint32_t aDataSize)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint16_t status;
int8_t numOfRecord;
bool IsSubsequentOffsetAll32Bit;
size_t minDataSizeNeeded;
const uint8_t * cursor = aInputBuf;
const uint8_t * const end_of_data = aInputBuf + aDataSize;
mSize = 0;
// status(2) + one record (4) == 6
// status(2) + first record (12) + subsequent records == ....
if ((end_of_data - cursor) < 6)
{
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
}
memcpy(&status, cursor, 2);
cursor += 2;
numOfRecord = int8_t(status & 0xF);
IsSubsequentOffsetAll32Bit = (status & 0x10) ? true : false;
if (0 != (status >> 5))
{
WeaveLogDetail(TimeService, "TimeZoneUtcOffset::Decode not all reserved bits are zero: 0x%X", status);
}
if (numOfRecord > WEAVE_CONFIG_TIME_NUM_UTC_OFFSET_RECORD)
{
WeaveLogDetail(TimeService, "TimeZoneUtcOffset::Decode received more offset records than we can store: %d",
numOfRecord);
numOfRecord = WEAVE_CONFIG_TIME_NUM_UTC_OFFSET_RECORD;
}
if (0 == numOfRecord)
{
// there must be at least one record
ExitNow(err = WEAVE_ERROR_INVALID_LIST_LENGTH);
}
else if (1 == numOfRecord)
{
minDataSizeNeeded = 4;
// note IsSubsequentOffsetAll32Bit shall be zero in this case, but we skipping verification here to save code space
if (size_t(end_of_data - cursor) < minDataSizeNeeded)
{
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
}
// set the timestamp to 0, as it will be ignored at calculation when mSize == 1, anyway
mUtcOffsetRecord[0].mBeginAt_usec = 0;
memcpy(&mUtcOffsetRecord[0].mUtcOffset_sec, cursor, 4);
cursor += 4;
}
else
{
if (IsSubsequentOffsetAll32Bit)
{
minDataSizeNeeded = 8 + 4 + (numOfRecord - 1) * 8;
}
else
{
minDataSizeNeeded = 8 + 4 + (numOfRecord - 1) * 6;
}
if (size_t(end_of_data - cursor) < minDataSizeNeeded)
{
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
}
memcpy(&mUtcOffsetRecord[0].mBeginAt_usec, cursor, 8);
cursor += 8;
memcpy(&mUtcOffsetRecord[0].mUtcOffset_sec, cursor, 4);
cursor += 4;
for (int i = 1; i < numOfRecord; ++i)
{
int32_t temp32;
memcpy(&temp32, cursor, 4);
cursor += 4;
mUtcOffsetRecord[i].mBeginAt_usec = mUtcOffsetRecord[i - 1].mBeginAt_usec + timesync_t(temp32) * 1000000;
if (IsSubsequentOffsetAll32Bit)
{
memcpy(&temp32, cursor, 4);
cursor += 4;
}
else
{
int16_t temp16;
memcpy(&temp16, cursor, 2);
cursor += 2;
// sign extension shall happen here
temp32 = temp16;
}
mUtcOffsetRecord[i].mUtcOffset_sec = mUtcOffsetRecord[i - 1].mUtcOffset_sec + temp32;
}
}
mSize = numOfRecord;
exit:
// We update the cursor out of good hygiene,
// such that if the code is extended in the future such that the cursor is used,
// it will be in the correct position for such code.
IgnoreUnusedVariable(cursor);
WeaveLogFunctError(err);
return err;
}
WEAVE_ERROR TimeZoneUtcOffset::Encode(uint8_t * const aOutputBuf, uint32_t * const aDataSize)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint16_t status = 0;
bool IsSubsequentOffsetAll32Bit = false;
uint8_t * cursor = aOutputBuf;
{
const size_t buffer_size_needed = 2 + 8 + 4 + (mSize - 1) * 8;
if (*aDataSize < buffer_size_needed)
{
*aDataSize = buffer_size_needed;
ExitNow(err = WEAVE_ERROR_INVALID_MESSAGE_LENGTH);
}
}
*aDataSize = 0;
if ((mSize > 0xF) || (mSize > WEAVE_CONFIG_TIME_NUM_UTC_OFFSET_RECORD))
{
ExitNow(err = WEAVE_ERROR_INVALID_LIST_LENGTH);
}
status = mSize & 0xF;
if (mSize < 1)
{
ExitNow(err = WEAVE_ERROR_INVALID_LIST_LENGTH);
}
else if (1 == mSize)
{
// this means we don't use DST, so there is only one record to check and encode
// since there is only one and no subsequent record, the IsSubsequentOffsetAll32Bit is redundant and shall be 0
memcpy(cursor, &status, 2);
cursor += 2;
memcpy(cursor, &mUtcOffsetRecord[0].mUtcOffset_sec, 4);
cursor += 4;
}
else
{
// round 1: check if we need 4 bytes in the offsets
for (int i = 1; i < mSize; ++i)
{
const int32_t diff_sec = mUtcOffsetRecord[i].mUtcOffset_sec - mUtcOffsetRecord[i - 1].mUtcOffset_sec;
if ((diff_sec > INT16_MAX) || (diff_sec < INT16_MIN))
{
IsSubsequentOffsetAll32Bit = true;
}
}
if (IsSubsequentOffsetAll32Bit)
{
status |= (1 << 4);
}
memcpy(cursor, &status, 2);
cursor += 2;
memcpy(cursor, &mUtcOffsetRecord[0].mBeginAt_usec, 8);
cursor += 8;
memcpy(cursor, &mUtcOffsetRecord[0].mUtcOffset_sec, 4);
cursor += 4;
// round 2: fill in subsequent records
for (int i = 1; i < mSize; ++i)
{
int32_t temp32;
const timesync_t diff_timestamp_sec =
Platform::Divide((mUtcOffsetRecord[i].mBeginAt_usec - mUtcOffsetRecord[i - 1].mBeginAt_usec), 1000000);
if (diff_timestamp_sec <= 0)
{
ExitNow(err = WEAVE_ERROR_INCORRECT_STATE);
}
else if (diff_timestamp_sec > INT32_MAX)
{
ExitNow(err = WEAVE_ERROR_INCORRECT_STATE);
}
{
temp32 = int32_t(diff_timestamp_sec);
memcpy(cursor, &temp32, 4);
cursor += 4;
}
temp32 = mUtcOffsetRecord[i].mUtcOffset_sec - mUtcOffsetRecord[i - 1].mUtcOffset_sec;
if (IsSubsequentOffsetAll32Bit)
{
memcpy(cursor, &temp32, 4);
cursor += 4;
}
else
{
// sign shall be carried over
const int temp16 = temp32;
memcpy(cursor, &temp16, 2);
cursor += 2;
}
}
}
*aDataSize = cursor - aOutputBuf;
exit:
WeaveLogFunctError(err);
return err;
}
#endif // WEAVE_CONFIG_TIME