Skip to content

Commit

Permalink
ICU-21035 Pass ByteSink from Locale::getKeywordValue() to uloc_getKey…
Browse files Browse the repository at this point in the history
…wordValue().

This eliminates the need for a scratch buffer in Locale::getKeywordValue()
and also the need for counting bytes required in uloc_getKeywordValue(),
something that ByteSink will now handle correctly.
  • Loading branch information
roubert committed Sep 2, 2020
1 parent fcc3bcb commit f9ada4b
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 65 deletions.
43 changes: 1 addition & 42 deletions icu4c/source/common/locid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1488,48 +1488,7 @@ Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& sta
return;
}

LocalMemory<char> scratch;
int32_t scratch_capacity = 16; // Arbitrarily chosen default size.

char* buffer;
int32_t result_capacity, reslen;

for (;;) {
if (scratch.allocateInsteadAndReset(scratch_capacity) == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}

buffer = sink.GetAppendBuffer(
/*min_capacity=*/scratch_capacity,
/*desired_capacity_hint=*/scratch_capacity,
scratch.getAlias(),
scratch_capacity,
&result_capacity);

reslen = uloc_getKeywordValue(
fullName,
keywordName_nul.data(),
buffer,
result_capacity,
&status);

if (status != U_BUFFER_OVERFLOW_ERROR) {
break;
}

scratch_capacity = reslen;
status = U_ZERO_ERROR;
}

if (U_FAILURE(status)) {
return;
}

sink.Append(buffer, reslen);
if (status == U_STRING_NOT_TERMINATED_WARNING) {
status = U_ZERO_ERROR; // Terminators not used.
}
ulocimp_getKeywordValue(fullName, keywordName_nul.data(), sink, &status);
}

void
Expand Down
62 changes: 39 additions & 23 deletions icu4c/source/common/uloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,27 +719,51 @@ uloc_getKeywordValue(const char* localeID,
char* buffer, int32_t bufferCapacity,
UErrorCode* status)
{
if (buffer != nullptr) {
buffer[0] = '\0';
if (U_FAILURE(*status)) {
return 0;
}

CheckedArrayByteSink sink(buffer, bufferCapacity);
ulocimp_getKeywordValue(localeID, keywordName, sink, status);

int32_t reslen = sink.NumberOfBytesAppended();

if (U_FAILURE(*status)) {
return reslen;
}

if (sink.Overflowed()) {
*status = U_BUFFER_OVERFLOW_ERROR;
} else {
u_terminateChars(buffer, bufferCapacity, reslen, status);
}

return reslen;
}

U_CAPI void U_EXPORT2
ulocimp_getKeywordValue(const char* localeID,
const char* keywordName,
icu::ByteSink& sink,
UErrorCode* status)
{
const char* startSearchHere = NULL;
const char* nextSeparator = NULL;
char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN];
int32_t result = 0;

if(status && U_SUCCESS(*status) && localeID) {
char tempBuffer[ULOC_FULLNAME_CAPACITY];
const char* tmpLocaleID;

if (keywordName == NULL || keywordName[0] == 0) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
return;
}

locale_canonKeywordName(keywordNameBuffer, keywordName, status);
if(U_FAILURE(*status)) {
return 0;
return;
}

if (_hasBCP47Extension(localeID)) {
Expand All @@ -751,7 +775,7 @@ uloc_getKeywordValue(const char* localeID,
startSearchHere = locale_getKeywordsStart(tmpLocaleID);
if(startSearchHere == NULL) {
/* no keywords, return at once */
return 0;
return;
}

/* find the first keyword */
Expand All @@ -763,7 +787,7 @@ uloc_getKeywordValue(const char* localeID,
nextSeparator = uprv_strchr(startSearchHere, '=');
if(!nextSeparator) {
*status = U_ILLEGAL_ARGUMENT_ERROR; /* key must have =value */
return 0;
return;
}
/* strip leading & trailing spaces (TC decided to tolerate these) */
while(*startSearchHere == ' ') {
Expand All @@ -777,20 +801,20 @@ uloc_getKeywordValue(const char* localeID,
/* copy & normalize keyName from locale */
if (startSearchHere == keyValueTail) {
*status = U_ILLEGAL_ARGUMENT_ERROR; /* empty keyword name in passed-in locale */
return 0;
return;
}
keyValueLen = 0;
while (startSearchHere < keyValueTail) {
if (!UPRV_ISALPHANUM(*startSearchHere)) {
*status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed keyword name */
return 0;
return;
}
if (keyValueLen < ULOC_KEYWORD_BUFFER_LEN - 1) {
localeKeywordNameBuffer[keyValueLen++] = uprv_tolower(*startSearchHere++);
} else {
/* keyword name too long for internal buffer */
*status = U_INTERNAL_PROGRAM_ERROR;
return 0;
return;
}
}
localeKeywordNameBuffer[keyValueLen] = 0; /* terminate */
Expand All @@ -811,28 +835,20 @@ uloc_getKeywordValue(const char* localeID,
/* Now copy the value, but check well-formedness */
if (nextSeparator == keyValueTail) {
*status = U_ILLEGAL_ARGUMENT_ERROR; /* empty key value name in passed-in locale */
return 0;
return;
}
keyValueLen = 0;
while (nextSeparator < keyValueTail) {
if (!UPRV_ISALPHANUM(*nextSeparator) && !UPRV_OK_VALUE_PUNCTUATION(*nextSeparator)) {
*status = U_ILLEGAL_ARGUMENT_ERROR; /* malformed key value */
return 0;
}
if (keyValueLen < bufferCapacity) {
/* Should we lowercase value to return here? Tests expect as-is. */
buffer[keyValueLen++] = *nextSeparator++;
} else { /* keep advancing so we return correct length in case of overflow */
keyValueLen++;
nextSeparator++;
return;
}
/* Should we lowercase value to return here? Tests expect as-is. */
sink.Append(nextSeparator++, 1);
}
result = u_terminateChars(buffer, bufferCapacity, keyValueLen, status);
return result;
return;
}
}
}
return 0;
}

U_CAPI int32_t U_EXPORT2
Expand Down
6 changes: 6 additions & 0 deletions icu4c/source/common/ulocimp.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ ulocimp_canonicalize(const char* localeID,
icu::ByteSink& sink,
UErrorCode* err);

U_CAPI void U_EXPORT2
ulocimp_getKeywordValue(const char* localeID,
const char* keywordName,
icu::ByteSink& sink,
UErrorCode* status);

/**
* Writes a well-formed language tag for this locale ID.
*
Expand Down

0 comments on commit f9ada4b

Please sign in to comment.