* 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_item.h"
#include "common/qrcodegen/qrcode_generator.h"
#include "common/qrcodegen/qrcode_stream.h"
#include "common/qrcodegen/qrcode_version.h"
#include "common/qrcodegen/qrcode_list.h"
#include "securec.h"
#define THIRD_ENCODE 3
#define THIRD_ENCODE_TWO 2
#define THIRD_ENCODE_TWO_A 45
bool QrcodeIsValidMode(QrcodeMode mode)
{
return ((mode >= QRCODE_MODE_NUM) && (mode <= QRCODE_MODE_8));
}
static QrcodeItem *QrcodeItemNew(QrcodeMode mode, int32_t size, const uint8_t *data)
{
if (QrcodeItemCheck(mode, size, data)) {
return nullptr;
}
QrcodeItem *item = (QrcodeItem *)QrcodeMalloc(sizeof(QrcodeItem));
if (item == nullptr) {
return nullptr;
}
if (memset_s(item, sizeof(QrcodeItem), 0, sizeof(QrcodeItem)) != 0) {
QrcodeFree(item);
return nullptr;
}
item->mode = mode;
item->size = size;
if (size > 0) {
item->data = (uint8_t *)QrcodeMalloc(size);
if (item->data == nullptr) {
QrcodeFree(item);
return nullptr;
}
if (memcpy_s(item->data, size, data, size) != 0) {
QrcodeFree(item->data);
QrcodeFree(item);
return nullptr;
}
}
return item;
}
static void QrcodeItemFree(QrcodeItem *item)
{
if (item) {
QrcodeFree(item->data);
QrcodeFree(item);
}
}
QrcodeItemList *QrcodeItemListNew()
{
QrcodeItemList *list = (QrcodeItemList *)QrcodeMalloc(sizeof(QrcodeItemList));
if (list == nullptr) {
return nullptr;
}
QrcodeListInit(&list->list);
list->version = 0;
return list;
}
int32_t QrcodeItemListAdd(QrcodeItemList *list, QrcodeMode mode, int32_t size, const uint8_t *data)
{
QrcodeItem *item = nullptr;
if (list == nullptr) {
return -1;
}
item = QrcodeItemNew(mode, size, data);
if (item == nullptr) {
return -1;
}
QrCodeListTailInsert(&list->list, &item->next);
return 0;
}
void QrcodeItemListFree(QrcodeItemList *list)
{
QrcodeItem *pos = nullptr;
if ((list == nullptr) || QrCodeListEmpty(&list->list)) {
return;
}
QrcodeDlListIter iter;
QrcodeDlListIterInitTyped(&iter, &list->list, &((QrcodeItem *)0)->next);
while (QrcodeDlListIterHasNext(&iter)) {
pos = (QrcodeItem *)QrcodeDlListIterNextSafe(&iter);
QrcodeItemFree(pos);
}
}
#define QT_VAL_HUNDRED 100
#define QT_VAL_TEN 10
static int32_t QrcodeItemEncodeNum(QrcodeStream *stream, QrcodeItem *item, int32_t version)
{
int32_t ret = QrcodeStreamAddNum(stream, 4, QRCODE_VERSION_MODEID_NUM);
if (ret < 0) {
return -1;
}
if (item == nullptr) {
return -1;
}
ret = QrcodeStreamAddNum(stream, QrcodeVersionModeLength(QRCODE_MODE_NUM, version), item->size);
if (ret < 0) {
return -1;
}
uint32_t val = 0;
int32_t words = item->size / THIRD_ENCODE;
for (int32_t i = 0; i < words; i++) {
val = (item->data[i * THIRD_ENCODE] - '0') * QT_VAL_HUNDRED;
val += (item->data[i * THIRD_ENCODE + SET_BIT_ONE] - '0') * QT_VAL_TEN;
val += (item->data[i * THIRD_ENCODE + SET_BIT_TWO] - '0');
ret = QrcodeStreamAddNum(stream, SET_BIT_TEN, val);
if (ret < 0) {
return -1;
}
}
if ((item->size - words * THIRD_ENCODE) == QR_BIT_ONE) {
val = item->data[words * THIRD_ENCODE] - '0';
ret = QrcodeStreamAddNum(stream, SET_BIT_FOUR, val);
if (ret < 0) {
return -1;
}
} else if ((item->size - words * THIRD_ENCODE) == QR_BIT_TWO) {
val = (item->data[words * THIRD_ENCODE] - '0') * QT_VAL_TEN;
val += (item->data[words * THIRD_ENCODE + SET_BIT_ONE] - '0');
ret = QrcodeStreamAddNum(stream, SET_BIT_SEVEN, val);
if (ret < 0) {
return -1;
}
}
return 0;
}
const int8_t QRCODE_ALPHA_BET_TABLE[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
int32_t QrcodeEstimateNum(int32_t size)
{
int32_t width = size / THIRD_ENCODE;
int32_t bitCount = width * SET_BIT_TEN;
switch (size - width * THIRD_ENCODE) {
case QR_BIT_ONE:
bitCount += SET_BIT_FOUR;
break;
case QR_BIT_TWO:
bitCount += SET_BIT_SEVEN;
break;
default:
break;
}
return bitCount;
}
int32_t QrcodeEstimateAlphaBet(int32_t size)
{
int32_t width = size / QR_BIT_TWO;
int32_t bitCount = width * (SET_BIT_SEVEN + SET_BIT_FOUR);
if (((uint32_t)size) & 1) {
bitCount += 6;
}
return bitCount;
}
static int32_t QRItemEncodeAlphabet(QrcodeStream *stream, QrcodeItem *item, int32_t version)
{
if ((stream == nullptr) || (item == nullptr)) {
return -1;
}
int32_t ret = QrcodeStreamAddNum(stream, 4, QRCODE_VERSION_MODEID_AN);
if (ret < 0) {
return -1;
}
ret = QrcodeStreamAddNum(stream, QrcodeVersionModeLength(QRCODE_MODE_AN, version), item->size);
if (ret < 0) {
return -1;
}
int32_t words = item->size / 2;
uint32_t val = 0;
for (int32_t i = 0; i < words; i++) {
val = (uint32_t)QrcodeLookAtTable(item->data[i * THIRD_ENCODE_TWO]) * THIRD_ENCODE_TWO_A;
val += (uint32_t)QrcodeLookAtTable(item->data[i * THIRD_ENCODE_TWO + 1]);
ret = QrcodeStreamAddNum(stream, (SET_BIT_SEVEN + SET_BIT_FOUR), val);
if (ret < 0) {
return -1;
}
}
if (((uint32_t)item->size) & 1) {
val = (uint32_t)QrcodeLookAtTable(item->data[words * THIRD_ENCODE_TWO]);
ret = QrcodeStreamAddNum(stream, SET_BIT_SIX, val);
if (ret < 0) {
return -1;
}
}
return 0;
}
int32_t QrcodeEstimate8(int32_t size)
{
return size * QR_BIT_EIGHT;
}
static int32_t QRItemEncode8(QrcodeStream *stream, QrcodeItem *item, int32_t version)
{
int32_t ret = 0;
if ((stream == nullptr) || (item == nullptr)) {
return -1;
}
ret = QrcodeStreamAddNum(stream, SET_BIT_FOUR, QRCODE_VERSION_MODEID_8);
if (ret < 0) {
return -1;
}
ret = QrcodeStreamAddNum(stream, QrcodeVersionModeLength(QRCODE_MODE_8, version), item->size);
if (ret < 0) {
return -1;
}
ret = QrcodeStreamAddBytes(stream, item->size, item->data);
if (ret < 0) {
return -1;
}
return 0;
}
int32_t QrcodeItemCheck(QrcodeMode mode, int32_t size, const uint8_t *data)
{
if ((size <= 0) || (data == nullptr)) {
return -1;
}
switch (mode) {
case QRCODE_MODE_NUM: {
for (int32_t i = 0; i < size; i++) {
if (data[i] < '0' || data[i] > '9') {
return -1;
}
}
return 0;
}
case QRCODE_MODE_AN: {
for (int32_t i = 0; i < size; i++) {
if (QrcodeLookAtTable(data[i]) < 0) {
return -1;
}
}
return 0;
}
case QRCODE_MODE_8:
return 0;
case QRCODE_MODE_NUL:
break;
default:
break;
}
return -1;
}
static int32_t QRItemGetBitSize(QrcodeItem *item, int32_t version)
{
int32_t bitCount = 0;
if (item == nullptr) {
return 0;
}
if (version == 0) {
version = 1;
}
switch (item->mode) {
case QRCODE_MODE_NUM:
bitCount = QrcodeEstimateNum(item->size);
break;
case QRCODE_MODE_AN:
bitCount = QrcodeEstimateAlphaBet(item->size);
break;
case QRCODE_MODE_8:
bitCount = QrcodeEstimate8(item->size);
break;
default:
return 0;
}
int32_t len = QrcodeVersionModeLength(item->mode, version);
int32_t m = 1U << ((uint32_t)len);
int32_t num = (item->size + m - 1) / m;
bitCount += num * (MODE_INDICATOR_SIZE + len);
return bitCount;
}
static int32_t QRItemListGetBitStreamSize(QrcodeItemList *list, int32_t version)
{
QrcodeItem *pos = nullptr;
int bits = 0;
if ((list == nullptr) || QrCodeListEmpty(&list->list)) {
return 0;
}
QrcodeDlListIter iter;
QrcodeDlListIterInitTyped(&iter, &list->list, &((QrcodeItem *)0)->next);
while (QrcodeDlListIterHasNext(&iter)) {
pos = (QrcodeItem *)QrcodeDlListIterNext(&iter);
bits += QRItemGetBitSize(pos, version);
}
return bits;
}
static int32_t QRItemListGetMinVersion(QrcodeItemList *list)
{
int32_t bitCount = 0;
int32_t version = 0;
int32_t prev = 0;
version = 0;
do {
prev = version;
bitCount = QRItemListGetBitStreamSize(list, prev);
version = QrcodeVersionGetMinVersion((bitCount + SET_BIT_SEVEN) / SET_BIT_EIGHT);
if (version < 0) {
return -1;
}
} while (version > prev);
return version;
}
static int32_t QRItemItemToStreamMode(QrcodeStream *stream, QrcodeItem *item, int32_t version)
{
if (item == nullptr) {
return 0;
}
switch (item->mode) {
case QRCODE_MODE_NUM:
return QrcodeItemEncodeNum(stream, item, version);
case QRCODE_MODE_AN:
return QRItemEncodeAlphabet(stream, item, version);
case QRCODE_MODE_8:
return QRItemEncode8(stream, item, version);
default:
break;
}
return 0;
}
static int32_t QrcodeItemItemToStream(QrcodeStream *stream, QrcodeItem *item, int32_t version)
{
int32_t ret = 0;
QrcodeItem *st1 = nullptr;
QrcodeItem *st2 = nullptr;
if ((stream == nullptr) || (item == nullptr)) {
return -1;
}
int32_t words = QrcodeVersionMaxWordsInMode(version, item->mode);
if (words != 0 && item->size > words) {
st1 = QrcodeItemNew(item->mode, words, item->data);
if (st1 == nullptr) {
goto ABORT;
}
st2 = QrcodeItemNew(item->mode, item->size - words, &item->data[words]);
if (st2 == nullptr) {
goto ABORT;
}
ret = QrcodeItemItemToStream(stream, st1, version);
if (ret < 0) {
goto ABORT;
}
ret = QrcodeItemItemToStream(stream, st2, version);
if (ret < 0) {
goto ABORT;
}
QrcodeItemFree(st1);
QrcodeItemFree(st2);
} else {
if (QRItemItemToStreamMode(stream, item, version) < 0) {
return -1;
}
}
return ret;
ABORT:
QrcodeItemFree(st1);
QrcodeItemFree(st2);
return -1;
}
static int32_t QrcodeItemListFillStream(QrcodeStream *stream, QrcodeItemList *list)
{
QrcodeItem *pos = nullptr;
if ((list == nullptr) || QrCodeListEmpty(&list->list)) {
return -1;
}
QrcodeDlListIter iter;
QrcodeDlListIterInitTyped(&iter, &list->list, &((QrcodeItem *)0)->next);
while (QrcodeDlListIterHasNext(&iter)) {
pos = (QrcodeItem *)QrcodeDlListIterNext(&iter);
if (QrcodeItemItemToStream(stream, pos, list->version) < 0) {
return -1;
}
}
return 0;
}
static int32_t QrcodeItemListListToStream(QrcodeStream *stream, QrcodeItemList *list)
{
if (list == nullptr) {
return -1;
}
int32_t ver = QRItemListGetMinVersion(list);
if (ver > list->version) {
list->version = ver;
}
int32_t retryTimesCount = 0;
while (retryTimesCount <= QRCODE_VERSION_MAX) {
QrcodeStreamClean(stream);
if (QrcodeItemListFillStream(stream, list) < 0) {
return -1;
}
ver = QrcodeVersionGetMinVersion((stream->bitCount + SET_BIT_SEVEN) / QR_BIT_NINE);
if (ver < 0) {
return -1;
} else if (ver > list->version) {
list->version = ver;
} else {
break;
}
retryTimesCount++;
}
if (retryTimesCount > QRCODE_VERSION_MAX) {
return -1;
}
return 0;
}
static int32_t QRItemListAddPaddingBitInit(QrcodeStream *stream, int32_t version, int32_t *bits, int32_t *maxwords,
int32_t *maxbits)
{
if ((maxwords == nullptr) || (maxbits == nullptr)) {
return -1;
}
*bits = stream->bitCount;
*maxwords = QrcodeVersionGetDataLen(version);
*maxbits = (*maxwords) * SET_BIT_EIGHT;
if (*maxbits < *bits) {
return -1;
}
if (*maxbits == *bits) {
return 0;
}
return 1;
}
static int32_t QRItemListAddPaddingBit(QrcodeStream *stream, int32_t version)
{
int32_t bits = 0;
int32_t maxbits = 0;
int32_t maxwords = 0;
int32_t ret = 0;
uint8_t *buf = nullptr;
int32_t words = 0;
QrcodeStream *padding = nullptr;
int32_t padLen = 0;
ret = QRItemListAddPaddingBitInit(stream, version, &bits, &maxwords, &maxbits);
if (ret <= 0) {
return ret;
}
if (maxbits - bits <= SET_BIT_FOUR) {
ret = QrcodeStreamAddNum(stream, maxbits - bits, 0);
goto DONE;
}
words = (bits + SET_BIT_FOUR + SET_BIT_SEVEN) / QR_BIT_EIGHT;
padding = QrcodeStreamNew();
if (padding == nullptr) {
return -1;
}
ret = QrcodeStreamAddNum(padding, words * QR_BIT_EIGHT - bits, 0);
if (ret < 0) {
goto DONE;
}
padLen = maxwords - words;
if (padLen > 0) {
buf = (uint8_t *)QrcodeMalloc(padLen);
if (buf == nullptr) {
ret = -1;
goto DONE;
}
for (int32_t i = 0; i < padLen; i++) {
buf[i] = (((uint32_t)i) & 1) ? 0x11 : 0xec;
}
ret = QrcodeStreamAddBytes(padding, padLen, buf);
QrcodeFree(buf);
if (ret < 0) {
goto DONE;
}
}
ret = QrcodeStreamAdd(stream, padding);
DONE:
QrcodeStreamFree(padding);
return ret;
}
static QrcodeStream *QRItemListMergeBitStream(QrcodeItemList *list)
{
if ((list == nullptr) || QrCodeListEmpty(&list->list)) {
return nullptr;
}
QrcodeStream *stream = QrcodeStreamNew();
if (stream == nullptr) {
return nullptr;
}
if (QrcodeItemListListToStream(stream, list) < 0) {
QrcodeStreamFree(stream);
return nullptr;
}
return stream;
}
uint8_t *QrcodeItemListGetByteStream(QrcodeItemList *list)
{
QrcodeStream *stream = QRItemListMergeBitStream(list);
if (stream == nullptr) {
return nullptr;
}
if (QRItemListAddPaddingBit(stream, list->version) < 0) {
QrcodeStreamFree(stream);
return nullptr;
}
uint8_t *array = QrcodeStreamDupData(stream);
QrcodeStreamFree(stream);
return array;
}