Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tlv] Implemented Getter for Localized String Identifier #24528

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/lib/core/DataModelTypes.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
* Copyright (c) 2021-2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,6 +41,7 @@ typedef uint64_t FabricId;
typedef uint8_t FabricIndex;
typedef uint32_t FieldId;
typedef uint16_t ListIndex;
typedef uint16_t LocalizedStringIdentifier;
typedef uint32_t TransactionId;
typedef uint16_t KeysetId;
typedef uint8_t InteractionModelRevision;
Expand Down
49 changes: 47 additions & 2 deletions src/lib/core/TLVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <lib/core/CHIPEncoding.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/core/TLV.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
Expand Down Expand Up @@ -298,10 +299,13 @@ CHIP_ERROR TLVReader::Get(ByteSpan & v)
return CHIP_NO_ERROR;
}

namespace {
constexpr int kUnicodeInformationSeparator1 = 0x1F;
constexpr size_t kMaxLocalizedStringIdentifierLen = 2 * sizeof(LocalizedStringIdentifier);
} // namespace

CHIP_ERROR TLVReader::Get(CharSpan & v)
{
constexpr int kUnicodeInformationSeparator1 = 0x1F;

if (!TLVTypeIsUTF8String(ElementType()))
{
return CHIP_ERROR_WRONG_TLV_TYPE;
Expand All @@ -324,6 +328,47 @@ CHIP_ERROR TLVReader::Get(CharSpan & v)
return CHIP_NO_ERROR;
}

CHIP_ERROR TLVReader::Get(Optional<LocalizedStringIdentifier> & lsid)
{
lsid.ClearValue();
VerifyOrReturnError(TLVTypeIsUTF8String(ElementType()), CHIP_ERROR_WRONG_TLV_TYPE);

const uint8_t * bytes;
ReturnErrorOnFailure(GetDataPtr(bytes)); // Does length sanity checks

uint32_t len = GetLength();

const uint8_t * infoSeparator1 = reinterpret_cast<const uint8_t *>(memchr(bytes, kUnicodeInformationSeparator1, len));
emargolis marked this conversation as resolved.
Show resolved Hide resolved
emargolis marked this conversation as resolved.
Show resolved Hide resolved
if (infoSeparator1 == nullptr)
{
return CHIP_NO_ERROR;
}

const uint8_t * lsidPtr = infoSeparator1 + 1;
len -= static_cast<uint32_t>(lsidPtr - bytes);

const uint8_t * infoSeparator2 = reinterpret_cast<const uint8_t *>(memchr(lsidPtr, kUnicodeInformationSeparator1, len));
emargolis marked this conversation as resolved.
Show resolved Hide resolved
if (infoSeparator2 != nullptr)
{
len = static_cast<uint32_t>(infoSeparator2 - lsidPtr);
}
if (len == 0)
{
return CHIP_NO_ERROR;
}
VerifyOrReturnError(len <= kMaxLocalizedStringIdentifierLen, CHIP_ERROR_INVALID_TLV_ELEMENT);

emargolis marked this conversation as resolved.
Show resolved Hide resolved
char idStr[kMaxLocalizedStringIdentifierLen] = { '0', '0', '0', '0' };
memcpy(&idStr[kMaxLocalizedStringIdentifierLen - len], lsidPtr, len);

LocalizedStringIdentifier id;
VerifyOrReturnError(Encoding::UppercaseHexToUint16(idStr, sizeof(idStr), id) == sizeof(LocalizedStringIdentifier),
CHIP_ERROR_INVALID_TLV_ELEMENT);

lsid.SetValue(id);
return CHIP_NO_ERROR;
}

CHIP_ERROR TLVReader::GetBytes(uint8_t * buf, size_t bufSize)
{
if (!TLVTypeIsString(ElementType()))
Expand Down
18 changes: 18 additions & 0 deletions src/lib/core/TLVReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@

#pragma once

#include <lib/core/DataModelTypes.h>
#include <lib/core/Optional.h>

#include "TLVCommon.h"

#include "TLVWriter.h"
Expand Down Expand Up @@ -472,6 +475,21 @@ class DLL_EXPORT TLVReader
*/
CHIP_ERROR Get(CharSpan & v);

/**
* Get the value of the Localized String Identifier.
emargolis marked this conversation as resolved.
Show resolved Hide resolved
*
* The method takes what's after the first Information Separator 1 <IS1>, and until end of string
* or second <IS1>, and return the hex-decoded string identifier, if one was there.
*
* @param[out] lsid Optional Localized String Identifier. Returns empty
* if the value is not found or it was invalidly encoded.
*
* @retval #CHIP_NO_ERROR If the method succeeded.
* @retval #CHIP_ERROR_WRONG_TLV_TYPE If the current element is not a TLV character string, or
* the reader is not positioned on an element.
*/
emargolis marked this conversation as resolved.
Show resolved Hide resolved
CHIP_ERROR Get(Optional<LocalizedStringIdentifier> & lsid);

/**
* Get the value of the current element as an enum value, if it's an integer
* value that fits in the enum type.
Expand Down
72 changes: 66 additions & 6 deletions src/lib/core/tests/TestTLV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2789,6 +2789,8 @@ void CheckTLVByteSpan(nlTestSuite * inSuite, void * inContext)
NL_TEST_ASSERT(inSuite, memcmp(readerSpan.data(), bytesBuffer, sizeof(bytesBuffer)) == 0);
}

#define IS1_CHAR "\x1F"

void CheckTLVCharSpan(nlTestSuite * inSuite, void * inContext)
{
struct CharSpanTestCase
Expand All @@ -2799,12 +2801,14 @@ void CheckTLVCharSpan(nlTestSuite * inSuite, void * inContext)

// clang-format off
static CharSpanTestCase sCharSpanTestCases[] = {
// Test String Expected String from Get()
// =========================================================================================
{ "This is a test case #0", "This is a test case #0" },
{ "This is a test case #1\x1fTest Localized String Identifier", "This is a test case #1" },
{ "This is a test case #2 \x1f abc \x1f def", "This is a test case #2 " },
{ "This is a test case #3\x1f", "This is a test case #3" },
// Test String Expected String from Get()
// ==================================================================================================
{ "This is a test case #0", "This is a test case #0" },
{ "This is a test case #1" IS1_CHAR "Test Localized String Identifier", "This is a test case #1" },
{ "This is a test case #2 " IS1_CHAR "abc" IS1_CHAR "def", "This is a test case #2 " },
{ "This is a test case #3" IS1_CHAR, "This is a test case #3" },
{ "Thé" IS1_CHAR, "Thé" },
{ IS1_CHAR " abc " IS1_CHAR " def", "" },
};
// clang-format on

Expand Down Expand Up @@ -2836,6 +2840,61 @@ void CheckTLVCharSpan(nlTestSuite * inSuite, void * inContext)
}
}

void CheckTLVGetLocalizedStringIdentifier(nlTestSuite * inSuite, void * inContext)
{
struct CharSpanTestCase
{
const char * testString;
Optional<LocalizedStringIdentifier> expectedLSID;
CHIP_ERROR expectedResult;
};

// clang-format off
static CharSpanTestCase sCharSpanTestCases[] = {
// Test String Expected LocalizedStringIdentifier from Get() Expected Return
// =============================================================================================================================================
{ "This is a test case #0", chip::Optional<LocalizedStringIdentifier>(), CHIP_NO_ERROR },
{ "This is a test case #1" IS1_CHAR "0123", chip::Optional<LocalizedStringIdentifier>(0x0123), CHIP_NO_ERROR },
{ "This is a test case #2" IS1_CHAR "0123" IS1_CHAR "3210", chip::Optional<LocalizedStringIdentifier>(0x0123), CHIP_NO_ERROR },
{ "This is a test case #3" IS1_CHAR "012", chip::Optional<LocalizedStringIdentifier>(0x012), CHIP_NO_ERROR },
{ "Thé" IS1_CHAR "", chip::Optional<LocalizedStringIdentifier>(), CHIP_NO_ERROR },
{ "Thé" IS1_CHAR "7", chip::Optional<LocalizedStringIdentifier>(0x7), CHIP_NO_ERROR },
{ "Thé" IS1_CHAR "1FA", chip::Optional<LocalizedStringIdentifier>(0x1FA), CHIP_NO_ERROR },
{ "" IS1_CHAR "1FA", chip::Optional<LocalizedStringIdentifier>(0x1FA), CHIP_NO_ERROR },
{ "Thé" IS1_CHAR "1FAB", chip::Optional<LocalizedStringIdentifier>(0x1FAB), CHIP_NO_ERROR },
{ "Thé" IS1_CHAR "1FAb", chip::Optional<LocalizedStringIdentifier>(), CHIP_ERROR_INVALID_TLV_ELEMENT },
{ "Thé" IS1_CHAR "1FABC", chip::Optional<LocalizedStringIdentifier>(), CHIP_ERROR_INVALID_TLV_ELEMENT },
{ "Thé" IS1_CHAR "1FA" IS1_CHAR "", chip::Optional<LocalizedStringIdentifier>(0x1FA), CHIP_NO_ERROR },
{ "Thé" IS1_CHAR "1FA" IS1_CHAR "F8sa===", chip::Optional<LocalizedStringIdentifier>(0x1FA), CHIP_NO_ERROR },
};
// clang-format on

for (auto & testCase : sCharSpanTestCases)
emargolis marked this conversation as resolved.
Show resolved Hide resolved
{
uint8_t backingStore[100];
TLVWriter writer;
TLVReader reader;
CHIP_ERROR err = CHIP_NO_ERROR;

writer.Init(backingStore);

err = writer.PutString(ProfileTag(TestProfile_1, 1), testCase.testString);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

err = writer.Finalize();
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

reader.Init(backingStore, writer.GetLengthWritten());
err = reader.Next();
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);

Optional<LocalizedStringIdentifier> readerLSID;
err = reader.Get(readerLSID);
NL_TEST_ASSERT(inSuite, testCase.expectedResult == err);
NL_TEST_ASSERT(inSuite, testCase.expectedLSID == readerLSID);
}
}

void CheckTLVSkipCircular(nlTestSuite * inSuite, void * inContext)
{
const size_t bufsize = 40; // large enough s.t. 2 elements fit, 3rd causes eviction
Expand Down Expand Up @@ -4514,6 +4573,7 @@ static const nlTest sTests[] =
NL_TEST_DEF("CHIP TLV Skip non-contiguous", CheckTLVSkipCircular),
NL_TEST_DEF("CHIP TLV ByteSpan", CheckTLVByteSpan),
NL_TEST_DEF("CHIP TLV CharSpan", CheckTLVCharSpan),
NL_TEST_DEF("CHIP TLV Get LocalizedStringIdentifier", CheckTLVGetLocalizedStringIdentifier),
NL_TEST_DEF("CHIP TLV Scoped Buffer", CheckTLVScopedBuffer),
NL_TEST_DEF("CHIP TLV Check reserve", CheckCloseContainerReserve),
NL_TEST_DEF("CHIP TLV Reader Fuzz Test", TLVReaderFuzzTest),
Expand Down