* Copyright (c) 2022-2026 Huawei Device Co., Ltd.
* 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.
*/
#include "common/qrcodegen/qrcode_generator.h"
#include "common/qrcodegen/qrcode_item.h"
#include "common/qrcodegen/qrcode_version.h"
#include "common/qrcodegen/qrcode_inner.h"
typedef struct {
const char *p;
const char *q;
QrcodeMode mode;
int32_t ret;
int32_t len;
int32_t dif;
int32_t la;
int32_t ln;
int32_t l8;
int32_t swcost;
} QrcodeStrGet8Data;
static inline bool IsNumber(uint8_t c)
{
return (unsigned char)(c - '0') < 10;
}
int32_t QrcodeLookAtTable(unsigned char c)
{
if (c & 0x80) {
return -1;
}
return QRCODE_ALPHA_BET_TABLE[(int32_t)c];
}
static inline int32_t IsAlphaBetNumber(unsigned char c)
{
return QrcodeLookAtTable(c) >= 0;
}
static QrcodeMode QrcodeStrGetMode(uint8_t c)
{
if (c == '\0') {
return QRCODE_MODE_NUL;
}
if (IsNumber(c)) {
return QRCODE_MODE_NUM;
} else if (IsAlphaBetNumber(c)) {
return QRCODE_MODE_AN;
}
return QRCODE_MODE_8;
}
static int32_t QrcodeStrGetNum(const char *string, QrcodeItemList *list);
static int32_t QrcodeStrGetAn(const char *string, QrcodeItemList *list);
static int32_t QrcodeStrGet8(const char *string, QrcodeItemList *list);
static int32_t QrcodeStrGetNum(const char *string, QrcodeItemList *list)
{
if (list == nullptr) {
return -1;
}
const char *p = string;
while (IsNumber(*p)) {
p++;
}
int32_t dif = 0;
int32_t ln = QrcodeVersionModeLength(QRCODE_MODE_NUM, list->version);
int32_t len = p - string;
QrcodeMode mode = QrcodeStrGetMode(*p);
if (mode == QRCODE_MODE_8) {
dif = QrcodeEstimateNum(len) + SET_BIT_FOUR + ln + QrcodeEstimate8(1) - QrcodeEstimate8(len + 1);
if (dif > 0) {
return QrcodeStrGet8(string, list);
}
}
if (mode == QRCODE_MODE_AN) {
dif = QrcodeEstimateNum(len) + SET_BIT_FOUR + ln + QrcodeEstimateAlphaBet(1) - QrcodeEstimateAlphaBet(len + 1);
if (dif > 0) {
return QrcodeStrGetAn(string, list);
}
}
if (QrcodeItemListAdd(list, QRCODE_MODE_NUM, len, (uint8_t *)string) < 0) {
return -1;
}
return len;
}
static int32_t QrcodeStrGetAn(const char *string, QrcodeItemList *list)
{
if (list == nullptr) {
return -1;
}
const char *p = string;
if (p == nullptr) {
return 0;
}
const char *q = nullptr;
int32_t ln = QrcodeVersionModeLength(QRCODE_MODE_NUM, list->version);
int32_t dif = 0;
while (IsAlphaBetNumber(*p)) {
if (IsNumber(*p)) {
q = p;
while (IsNumber(*q)) {
q++;
}
dif = QrcodeEstimateAlphaBet(p - string) + QrcodeEstimateNum(q - p) + SET_BIT_FOUR + ln +
(IsAlphaBetNumber(*q) ? (SET_BIT_FOUR + ln) : 0) - QrcodeEstimateAlphaBet(q - string);
if (dif < 0) {
break;
} else {
p = q;
}
} else {
p++;
}
}
int32_t len = p - string;
int32_t la = QrcodeVersionModeLength(QRCODE_MODE_AN, list->version);
if (*p && ((uint32_t)(int32_t)*p) < sizeof(QRCODE_ALPHA_BET_TABLE) && !IsAlphaBetNumber(*p)) {
dif = QrcodeEstimateAlphaBet(len) + SET_BIT_FOUR + la + QrcodeEstimate8(1) - QrcodeEstimate8(len + 1);
if (dif > 0) {
return QrcodeStrGet8(string, list);
}
}
if (QrcodeItemListAdd(list, QRCODE_MODE_AN, len, (uint8_t *)string) < 0) {
return -1;
}
return len;
}
static void QrcodeStrGet8Init(const char *string, QrcodeItemList *list, QrcodeStrGet8Data *strGet8Data)
{
if ((list == nullptr) || (strGet8Data == nullptr)) {
return;
}
strGet8Data->q = nullptr;
strGet8Data->mode = QRCODE_MODE_NUL;
strGet8Data->ret = 0;
strGet8Data->len = 0;
strGet8Data->dif = 0;
strGet8Data->swcost = 0;
strGet8Data->la = QrcodeVersionModeLength(QRCODE_MODE_AN, list->version);
strGet8Data->ln = QrcodeVersionModeLength(QRCODE_MODE_NUM, list->version);
strGet8Data->l8 = QrcodeVersionModeLength(QRCODE_MODE_8, list->version);
strGet8Data->p = string + 1;
}
static int32_t QrcodeStrGet8(const char *string, QrcodeItemList *list)
{
QrcodeStrGet8Data strGet8Data;
QrcodeStrGet8Init(string, list, &strGet8Data);
while (*(strGet8Data.p) != '\0') {
strGet8Data.mode = QrcodeStrGetMode(*(strGet8Data.p));
if (strGet8Data.mode == QRCODE_MODE_NUM) {
strGet8Data.q = strGet8Data.p;
while (IsNumber(*(strGet8Data.q))) {
strGet8Data.q++;
}
strGet8Data.swcost = (QrcodeStrGetMode(*(strGet8Data.q)) == QRCODE_MODE_8) ?
(SET_BIT_FOUR + strGet8Data.l8) : 0;
strGet8Data.dif = QrcodeEstimate8(strGet8Data.p - string) +
QrcodeEstimateNum(strGet8Data.q - strGet8Data.p) + SET_BIT_FOUR + strGet8Data.ln +
strGet8Data.swcost - QrcodeEstimate8(strGet8Data.q - string);
if (strGet8Data.dif < 0) {
break;
} else {
strGet8Data.p = strGet8Data.q;
}
} else if (strGet8Data.mode == QRCODE_MODE_AN) {
strGet8Data.q = strGet8Data.p;
while (IsAlphaBetNumber(*(strGet8Data.q))) {
strGet8Data.q++;
}
strGet8Data.swcost = (QrcodeStrGetMode(*(strGet8Data.q)) == QRCODE_MODE_8) ?
(SET_BIT_FOUR + strGet8Data.l8) : 0;
strGet8Data.dif = QrcodeEstimate8(strGet8Data.p - string) +
QrcodeEstimateAlphaBet(strGet8Data.q - strGet8Data.p) + SET_BIT_FOUR + strGet8Data.la +
strGet8Data.swcost - QrcodeEstimate8(strGet8Data.q - string);
if (strGet8Data.dif < 0) {
break;
} else {
strGet8Data.p = strGet8Data.q;
}
} else {
strGet8Data.p++;
}
}
strGet8Data.len = strGet8Data.p - string;
strGet8Data.ret = QrcodeItemListAdd(list, QRCODE_MODE_8, strGet8Data.len, (unsigned char *)string);
if (strGet8Data.ret < 0) {
return -1;
}
return strGet8Data.len;
}
int32_t QrcodeStrToItemList(const char *str, QrcodeItemList *list)
{
int32_t length = 0;
QrcodeMode mode = QRCODE_MODE_NUM;
const char *p = str;
if (p == nullptr) {
return -1;
}
while (*p != '\0') {
mode = QrcodeStrGetMode(*p);
if (mode == QRCODE_MODE_NUM) {
length = QrcodeStrGetNum(p, list);
} else if (mode == QRCODE_MODE_AN) {
length = QrcodeStrGetAn(p, list);
} else {
length = QrcodeStrGet8(p, list);
}
if (length == 0) {
break;
}
p = p + length;
if (length < 0) {
return -1;
}
}
return 0;
}