/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
/**
* @file
*
* This file defines some hex to string help methods.
*
*/
package stdx.encoding.hex
let HEX_ENCODE_CHARS_LOW = "0123456789abcdef".toArray()
/*
* Converting a hexadecimal array to a set of UInt8 (HexToString decoding).
*
* @param data of Array<Rune>.
* @return Parameters of Option<Array<UInt8>>.
*
* @since 0.17.4
*/
func decode(data: Array<UInt8>): Option<Array<UInt8>> {
var arrLength: Int64 = data.size
var index: Int64 = 0
var temp: Array<UInt8> = Array<UInt8>(arrLength / 2, repeat: 0)
var i: Int64 = 0
while (i < arrLength) {
var highFour: UInt8 = unhex(data[i])
i++
var lowFour: UInt8 = unhex(data[i])
i++
var str: UInt8 = UInt8((highFour << 4) | lowFour)
temp[index] = str
index++
}
return Option<Array<UInt8>>.Some(temp)
}
@OverflowWrapping
func unhex(c: UInt8): UInt8 {
match {
case b'0' <= c && c <= b'9' => UInt8(UInt32(c) - UInt32(r'0'))
case b'a' <= c && c <= b'f' => UInt8(UInt32(c) - UInt32(r'a') + UInt32(10))
case b'A' <= c && c <= b'F' => UInt8(UInt32(c) - UInt32(r'A') + UInt32(10))
case _ => UInt8(0)
}
}
/*
* Converts the UInt8 collection to a character string (HexToString encodinkg).
* ASCII: A (65)
* Binary code: 0100 _ 0001
* Regroup: 0000 _ 0100 0000 _ 0001
* Hexadecimal: 4 1
* Hex code: 41
*
* @param data of Array<UInt8>.
* @return Parameters of String.
*
* @since 0.17.4
*/
func encode(data: Array<UInt8>): String {
var chrLen: Int64 = data.size
if (chrLen <= 0) {
return ""
}
var chs: Array<UInt8> = Array<UInt8>(chrLen * 2, repeat: 0)
var index: Int64 = 0
for (i in 0..chrLen) {
chs[index] = HEX_ENCODE_CHARS_LOW[Int64(((data[i] & 0xf0) >> 4))]
index++
chs[index] = HEX_ENCODE_CHARS_LOW[Int64((data[i] & 0x0f))]
index++
}
return unsafe { String.fromUtf8Unchecked(chs) }
}
func processDataBeforeDecode(data: Array<UInt8>): Array<UInt8> {
var temp: Array<UInt8> = Array<UInt8>(data.size, repeat: 0)
var index: Int64 = 0
for (i in 0..data.size) {
let ch = data[i]
if (ch.isAsciiHex()) {
temp[index] = ch
index++
} else if (ch == b'\0') {
return Array<UInt8>()
} else if (ch == b'\r' || ch == b'\n' || ch == b' ' || ch == b'\t') {
continue
} else {
return Array<UInt8>()
}
}
if (index <= 0 || index % 2 != 0) {
return Array<UInt8>()
}
return temp[0..index]
}
/**
* Provides the Hex encoding conversion function and the HexString to ByteArray function. If decoding fails, Option is returned.
*
* @param data of String.
* @return Parameters of Option<Array<UInt8>>.
*
* @since 0.17.4
*/
public func fromHexString(data: String): Option<Array<Byte>> {
var strChArr: Array<UInt8> = unsafe { data.rawData() }
if (strChArr.size == 0) {
return Option<Array<UInt8>>.Some(Array<UInt8>())
}
var temp: Array<UInt8> = processDataBeforeDecode(strChArr)
if (temp.size == 0) {
return Option<Array<UInt8>>.None
}
return decode(temp)
}
/**
* Provides the ByteArray to HexString function. If the encoding fails, Option is returned.
*
* @param data of String.
* @return Parameters of String
*
* @since 0.17.4
*/
public func toHexString(data: Array<Byte>): String {
return encode(data)
}