Skip to content

Commit

Permalink
[tlv] Implemented Getter for Localized String Identifier
Browse files Browse the repository at this point in the history
New method TLVReader::Get(Optional<LocalizedStringIdentifier>&) returns
Localized String Identifier if present.

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.
  • Loading branch information
emargolis committed Jan 19, 2023
1 parent a722c18 commit a86ea4b
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 3 deletions.
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
51 changes: 49 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,49 @@ 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));
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));
if (infoSeparator2 != nullptr)
{
len = static_cast<uint32_t>(infoSeparator2 - lsidPtr);
}

if (len == 0 || len > kMaxLocalizedStringIdentifierLen)
{
return CHIP_NO_ERROR;
}

char idStr[kMaxLocalizedStringIdentifierLen] = { '0', '0', '0', '0' };
memcpy(&idStr[kMaxLocalizedStringIdentifierLen - len], lsidPtr, len);

LocalizedStringIdentifier id;
if (Encoding::UppercaseHexToUint16(idStr, sizeof(idStr), id) != sizeof(LocalizedStringIdentifier))
{
return CHIP_NO_ERROR;
}

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.
*
* 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 has 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.
*/
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
57 changes: 57 additions & 0 deletions src/lib/core/tests/TestTLV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2805,6 +2805,8 @@ void CheckTLVCharSpan(nlTestSuite * inSuite, void * inContext)
{ "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" },
{ "Thé\037", "Thé" },
{ "\x1f abc \x1f def", "" },
};
// clang-format on

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

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

// clang-format off
static CharSpanTestCase sCharSpanTestCases[] = {
// Test String Expected LocalizedStringIdentifier from Get()
// ==============================================================================================
{ "This is a test case #0", chip::Optional<LocalizedStringIdentifier>() },
{ "This is a test case #1\0370123", chip::Optional<LocalizedStringIdentifier>(0x0123) },
{ "This is a test case #2\0370123\0373210", chip::Optional<LocalizedStringIdentifier>(0x0123) },
{ "This is a test case #3\037012", chip::Optional<LocalizedStringIdentifier>(0x012) },
{ "Thé\037", chip::Optional<LocalizedStringIdentifier>() },
{ "Thé\0377", chip::Optional<LocalizedStringIdentifier>(0x7) },
{ "Thé\0371FA", chip::Optional<LocalizedStringIdentifier>(0x1FA) },
{ "\0371FA", chip::Optional<LocalizedStringIdentifier>(0x1FA) },
{ "Thé\0371FAB", chip::Optional<LocalizedStringIdentifier>(0x1FAB) },
{ "Thé\0371FAb", chip::Optional<LocalizedStringIdentifier>() },
{ "Thé\0371FABC", chip::Optional<LocalizedStringIdentifier>() },
{ "Thé\0371FA\037", chip::Optional<LocalizedStringIdentifier>(0x1FA) },
{ "Thé\0371FA\037F8sa===", chip::Optional<LocalizedStringIdentifier>(0x1FA) },
};
// clang-format on

for (auto & testCase : sCharSpanTestCases)
{
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, err == CHIP_NO_ERROR);
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 +4570,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

0 comments on commit a86ea4b

Please sign in to comment.