/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. All rights resvered.
*/
/**
* @file
* The file declars the ByteString class.
*/
package io4cj
let MAP: Array<UInt8> = [65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47]
let NULL_CHAR = unsafe{ zeroValue<Rune>() }
let HEX_DIGITS: Array<Byte> = "0123456789abcdef".toArray()
/**
* The class is ByteString inherited from Object & Hashable & ToString & Equatable<ByteString>
* @author liyanqing14
* @since 0.32.5
*/
public open class ByteString <: Object & Hashable & ToString & Equatable<ByteString> {
let data: Array<Byte>
protected var hashcode: Int64 = 0
protected var utf8: ?String = None
/**
* The Function is init constructor
*
* @param data of Array<Byte>
* @since 0.32.5
*/
public init (data: Array<Byte>){
this.data = data
}
init (data: String){
this.utf8 = data
this.data = data.toArray()
}
/**
* The Function is of
*
* @param data of Array<Byte>
*
* @return Type of ByteString
* @since 0.32.5
*/
@Frozen
public static func of (data: Array<Byte>): ByteString {
return ByteString(data.clone())
}
/**
* The Function is of
*
* @param target of Array<Byte>
* @param offset of Int64
* @param byteCount of Int64
*
* @return Type of ByteString
* @since 0.32.5
*/
@Frozen
public static func of(target: Array<Byte>, offset: Int64, byteCount: Int64): ByteString {
let bytes: Array<Byte> = Array<Byte>(byteCount, repeat: 0)
var i = 0
while (i < byteCount) {
bytes[i] = target[i + offset]
i++
}
return ByteString(bytes)
}
/**
* The Function is encodeUtf8
*
* @param s of String
*
* @return Type of ByteString
* @since 0.32.5
*/
// Returns a new byte string containing the {@code UTF-8} bytes of {@code s}. */
@Frozen
public static func encodeUtf8(s: String): ByteString {
return ByteString(s)
}
/**
* The Function is UTF8
*
* @return Type of String
* @since 0.32.5
*/
/** Constructs a new {@code String} by decoding the bytes as {@code UTF-8}. */
@Frozen
public func UTF8(): String {
match (this.utf8) {
case Some(v) => return v
case None =>
let result = unsafe {String.fromUtf8Unchecked(this.data)}
this.utf8 = result
return result
}
}
/**
* The Function is base64
*
* @return Type of String
* @since 0.32.5
*/
@Frozen
public func base64 (): String {
return encode(this.data, MAP)
}
@OverflowWrapping
protected func encode(input: Array<Byte>, map: Array<Byte>): String {
let length = (input.size + 2) / 3 * 4
let out = Array<Byte>(length, repeat: NULL)
var index = 0
var end = input.size - input.size % 3
for (i in 0..end:3) {
out[index] = map[(Int64(input[i]) & 255) >> 2]
index++
out[index] = map[(Int64(input[i]) & 3) << 4 | (Int64(input[i + 1]) & 255) >> 4]
index++
out[index] = map[(Int64(input[i + 1]) & 15) << 2 | (Int64(input[i + 2]) & 255) >> 6]
index++
out[index] = map[Int64(input[i + 2]) & 63]
index++
}
match (Int64(input.size % 3)) {
case 1 =>
out[index] = map[(Int64(input[end]) & 255) >> 2]
index++
out[index] = map[(Int64(input[end]) & 3) << 4]
index++
out[index] = 61
index++
out[index] = 61
index++
case 2 =>
out[index] = map[(Int64(input[end]) & 255) >> 2]
index++
out[index] = map[(Int64(input[end]) & 3) << 4 | (Int64(input[end + 1]) & 255) >> 4]
index++
out[index] = map[(Int64(input[end + 1]) & 15) << 2]
index++
out[index] = 61
index++
case _ => ()
}
return unsafe {String.fromUtf8Unchecked(out)}
}
/**
* The Function is decodeBase64
*
* @param base64 of String
*
* @return Type of ?ByteString
* @since 0.32.5
*/
@Frozen
public static func decodeBase64 (base64: String): ?ByteString {
match (decode(base64)) {
case Some(arr) => return ByteString(arr)
case None => return None
}
}
@OverflowWrapping
protected static func decode(input: String): ?Array<Byte> {
var limit = input.size
while (limit > 0) {
let c = Rune(UInt32(input[limit - 1]))
if (c != r'=' && c != r'\n' && c != r'\r' && c != r' ' && c != r'\t') {
break
}
limit--
}
let out = Array<Byte>(limit * 6 / 8, repeat: 0)
var outCount = 0
var inCount = 0
var word = 0
for (pos in 0..limit) {
let c = Rune(UInt32(input[pos]))
var bits = 0
if (c >= r'A' && c <= r'Z') {
bits = Int64(UInt32(c)) - 65
} else if (c >= r'a' && c <= r'z') {
bits = Int64(UInt32(c)) - 71
} else if (c >= r'0' && c <= r'9') {
bits = Int64(UInt32(c)) + 4
} else if (c == r'+' || c == r'-') {
bits = 62
} else if (c == r'/' || c == r'_') {
bits = 63
} else if (c == r'\n' || c == r'\r' || c == r' ' || c == r'\t') {
continue
} else {
return None
}
word = (word << 6) | Int64(Int8(bits))
inCount++;
if (inCount % 4 == 0) {
out[outCount] = UInt8(word >> 16)
outCount++
out[outCount] = UInt8(word >> 8)
outCount++
out[outCount] = UInt8(word)
outCount++
}
}
let lastWordChars = inCount % 4
if (lastWordChars == 1) {
return None
} else if (lastWordChars == 2) {
word = word << 12
out[outCount] = UInt8(word >> 16)
outCount++
} else if (lastWordChars == 3) {
word = word << 6;
out[outCount] = UInt8(word >> 16)
outCount++
out[outCount] = UInt8(word >> 8)
outCount++
}
if (outCount == out.size){
return out
}
return out[0..outCount]
}
/**
* The Function is hex
*
* @return Type of String
* @since 0.32.5
*/
@Frozen
public func hex(): String {
let chars: Array<Byte> = Array<Byte>(this.data.size * 2, repeat: NULL)
var c = 0
for (d in 0..this.data.size) {
chars[c] = HEX_DIGITS[Int64((data[d] >> 4) & 0x0f)]
c++
chars[c] = HEX_DIGITS[Int64(data[d] & 0x0f)]
c++
}
return String.fromUtf8(chars)
}
/**
* The Function is decodeHexdecodeHex
*
* @param hex of String
*
* @return Type of ByteString
* @since 0.32.5
*/
@OverflowWrapping
@Frozen
public static func decodeHex(hex: String): ByteString {
if (hex.size % 2 != 0) {
throw IllegalArgumentException("Unexpected hex string: ${hex}")
}
let result = Array<Byte>(hex.size / 2, repeat: NULL)
for (i in 0..result.size) {
let d1 = decodeHexDigit(Rune(UInt32(hex[i * 2]))) << 4
let d2 = decodeHexDigit(Rune(UInt32(hex[i * 2 + 1])))
result[i] = UInt8(d1 + d2)
}
return ByteString(result)
}
protected static func decodeHexDigit(c: Rune): UInt32 {
if (c >= r'0' && c <= r'9') {
return UInt32(c) - UInt32(r'0')
}
if (c >= r'a' && c <= r'f') {
return UInt32(c) - UInt32(r'a') + 10
}
if (c >= r'A' && c <= r'F') {
return UInt32(c) - UInt32(r'A') + 10
}
throw IllegalArgumentException("Unexpected hex digit: ${c}")
}
/**
* The Function is read
*
* @param input of InputStream
* @param byteCount of Int64
*
* @return Type of ByteString
* @since 0.32.5
*/
@Frozen
public static func read(input: InputStream, byteCount: Int64): ByteString {
if (byteCount < 0) {
throw IllegalArgumentException("byteCount < 0")
}
let arr = Array<Byte>(byteCount, repeat: 0)
try {
input.read(arr)
} catch (e: Exception) {
throw EOFException(e.toString())
}
return ByteString(arr)
}
/**
* The Function is read
*
* @param path of String
*
* @return Type of ByteString
* @since 0.32.5
*/
@Frozen
public static func read(path: String): ByteString {
let src = File(path, OpenMode.Read)
return read(src,src.length)
}
/**
* The Function is toAsciiLowercase
*
* @return Type of ByteString
* @since 0.32.5
*/
@Frozen
public func toAsciiLowercase (): ByteString {
let arr = this.data.clone()
var index = 0
while (index < this.data.size) {
arr[index] = UInt8(UInt32(Rune(this.data[index]).toAsciiLowerCase()))
index++
}
return ByteString(arr)
}
/**
* The Function is toAsciiUppercase
*
* @return Type of ByteString
* @since 0.32.5
*/
@Frozen
public func toAsciiUppercase (): ByteString {
let arr = this.data.clone()
var index = 0
while (index < this.data.size) {
arr[index] = UInt8(UInt32(Rune(this.data[index]).toAsciiUpperCase()))
index++
}
return ByteString(arr)
}
/**
* The Function is substring
*
* @param beginIndex of Int64
*
* @return Type of ByteString
* @since 0.32.5
*/
@Frozen
public func substring(beginIndex: Int64): ByteString {
return substring(beginIndex, data.size)
}
/**
* The Function is substring
*
* @param beginIndex of Int64
* @param endIndex of Int64
*
* @return Type of ByteString
* @since 0.32.5
*/
@Frozen
public func substring(beginIndex: Int64, endIndex: Int64): ByteString {
if (beginIndex < 0) {
throw IllegalArgumentException("beginIndex < 0")
}
if (endIndex > this.data.size) {
throw IllegalArgumentException("endIndex > size(${data.size})")
}
let subLen = endIndex - beginIndex
if (subLen < 0 ) {
throw IllegalArgumentException("endIndex : ${endIndex} < beginIndex : ${beginIndex}")
}
if (beginIndex == 0 && endIndex == this.data.size) {
return this
}
let copy = Array<Byte>(subLen, repeat: 0)
ArrayCopy(this.data, beginIndex, copy, 0, subLen)
return ByteString(copy)
}
/**
* The Function is getByte
*
* @param pos of Int64
*
* @return Type of Byte
* @since 0.32.5
*/
@Frozen
public func getByte (pos: Int64): Byte {
return this.data[pos]
}
/**
* let member size type is Int64 {
* @since 0.32.5
*/
public prop size: Int64 {
get () {
return this.data.size
}
}
/**
* The Function is toByteArray
*
* @return Type of Array<Byte>
* @since 0.32.5
*/
@Frozen
public func toByteArray (): Array<Byte> {
return this.data.clone()
}
@Frozen
func internalArray(): Array<Byte> {
return this.data
}
/**
* The Function is write
*
* @param outPath of String
* @param mode of OpenMode, and the Default value is Append
*
* @return Type of Unit
* @since 0.32.5
*/
@Frozen
public func write (outPath: String): Unit {
write(File(outPath, OpenMode.Append))
// write(File(outPath, Write, mode))
}
/**
* The Function is write
*
* @param out of OutputStream
*
* @return Type of Unit
* @since 0.32.5
*/
@Frozen
public func write (out: OutputStream): Unit {
out.write(this.data)
}
/**
* The Function is write
*
* @param buffer of Buffer
* @since 0.32.5
*/
@Frozen
public func write (buffer: Buffer) {
buffer.write(this.data, 0, data.size)
}
/**
* The Function is rangeEquals
*
* @param offset of Int64
* @param other of ByteString
* @param otherOffset of Int64
* @param byteCount of Int64
*
* @return Type of Bool
* @since 0.32.5
*/
@Frozen
public func rangeEquals(offset: Int64, other: ByteString, otherOffset: Int64, byteCount: Int64): Bool {
return other.rangeEquals(otherOffset, this.data, offset, byteCount)
}
/**
* The Function is rangeEquals
*
* @param offset of Int64
* @param other of Array<Byte>
* @param otherOffset of Int64
* @param byteCount of Int64
*
* @return Type of Bool
* @since 0.32.5
*/
@Frozen
public func rangeEquals(offset: Int64, other: Array<Byte>, otherOffset: Int64, byteCount: Int64): Bool {
return offset >= 0 && offset <= data.size - byteCount
&& otherOffset >= 0 && otherOffset <= other.size - byteCount
&& Util.arrayRangeEquals(data, offset, other, otherOffset, byteCount)
}
/**
* The Function is startsWith
*
* @param prefix of ByteString
*
* @return Type of Bool
* @since 0.32.5
*/
@Frozen
public func startsWith(prefix: ByteString): Bool {
return rangeEquals(0, prefix, 0, prefix.size)
}
/**
* The Function is startsWith
*
* @param prefix of Array<Byte>
*
* @return Type of Bool
* @since 0.32.5
*/
@Frozen
public func startsWith(prefix: Array<Byte>): Bool {
return rangeEquals(0, prefix, 0, prefix.size)
}
/**
* The Function is endsWith
*
* @param suffix of ByteString
*
* @return Type of Bool
* @since 0.32.5
*/
@Frozen
public func endsWith (suffix: ByteString):Bool {
return rangeEquals(this.size - suffix.size, suffix, 0, suffix.size)
}
/**
* The Function is endsWith
*
* @param suffix of Array<Byte>
*
* @return Type of Bool
* @since 0.32.5
*/
@Frozen
public func endsWith (suffix:Array<Byte>) :Bool {
return rangeEquals(this.size - suffix.size, suffix, 0, suffix.size)
}
/**
* The Function is indexOf
*
* @param other of ByteString
*
* @return Type of Int64
* @since 0.32.5
*/
@Frozen
public func indexOf(other: ByteString): Int64 {
return indexOf(other.internalArray(), 0)
}
/**
* The Function is indexOf
*
* @param other of ByteString
* @param fromIndex of Int64
*
* @return Type of Int64
* @since 0.32.5
*/
@Frozen
public func indexOf(other: ByteString, fromIndex: Int64): Int64 {
return indexOf(other.internalArray(), fromIndex)
}
/**
* The Function is indexOf
*
* @param other of Array<Byte>
*
* @return Type of Int64
* @since 0.32.5
*/
@Frozen
public func indexOf(other: Array<Byte>): Int64 {
return indexOf(other, 0)
}
/**
* The Function is indexOf
*
* @param other of Array<Byte>
* @param fromIndex of Int64
*
* @return Type of Int64
* @since 0.32.5
*/
@Frozen
public func indexOf(other: Array<Byte>, fromIndex: Int64): Int64 {
for (i in max(fromIndex, 0)..=data.size-other.size where Util.arrayRangeEquals(this.data, i, other, 0, other.size)) {
return i
}
return -1
}
/**
* The Function is lastIndexOf
*
* @param other of ByteString
*
* @return Type of Int64
* @since 0.32.5
*/
@Frozen
public func lastIndexOf(other: ByteString) : Int64 {
return lastIndexOf(other.internalArray(), this.size)
}
/**
* The Function is lastIndexOf
*
* @param other of ByteString
* @param fromIndex of Int64
*
* @return Type of Int64
* @since 0.32.5
*/
@Frozen
public func lastIndexOf(other: ByteString, fromIndex: Int64): Int64 {
return lastIndexOf(other.internalArray(), fromIndex)
}
/**
* The Function is lastIndexOf
*
* @param other of Array<Byte>
*
* @return Type of Int64
* @since 0.32.5
*/
@Frozen
public func lastIndexOf(other: Array<Byte>): Int64 {
return lastIndexOf(other, this.size)
}
/**
* The Function is lastIndexOf
*
* @param other of Array<Byte>
* @param fromIndex of Int64
*
* @return Type of Int64
* @since 0.32.5
*/
@Frozen
public func lastIndexOf(other: Array<Byte>, fromIndex: Int64): Int64 {
for (i in min(fromIndex, data.size - other.size)..=0:-1 where Util.arrayRangeEquals(this.data, i, other, 0, other.size)) {
return i
}
return -1
}
/**
* The Function is ==
*
* @param that of ByteString
*
* @return Type of Bool
* @since 0.32.5
*/
public operator func == (that: ByteString): Bool {
return this.equals(that)
}
/**
* The Function is !=
*
* @param that of ByteString
*
* @return Type of Bool
* @since 0.32.5
*/
public operator func != (that: ByteString): Bool {
return !(this == that)
}
/**
* The Function is equals
*
* @param o of ByteString
*
* @return Type of Bool
* @since 0.32.5
*/
@Frozen
public func equals (o: ByteString): Bool {
if (refEq(this, o)) {
return true
}
if (o.size != this.size) {
return false
}
for (index in 0..this.size where this.data[index] != o.data[index]) {
return false
}
return true
}
/**
* The Function is hashCode
*
* @return Type of Int64
* @since 0.32.5
*/
@OverflowWrapping
@Frozen
public func hashCode (): Int64 {
return if (this.hashcode != 0) {
this.hashcode
} else {
var result: Int32 = 1
for (i in 0..this.data.size) {
result = result * 31 + Int32(Int8(this.data[i]))
}
this.hashcode = Int64(result)
this.hashcode
}
}
/**
* The Function is toString
*
* @return Type of String
* @since 0.32.5
*/
@Frozen
public func toString (): String {
if ( this.data.size == 0) {
return "[size=0]"
}
let text = UTF8()
let safeText = text.replace("\\", "\\\\")
.replace("\n", "\\n")
.replace("\r", "\\r")
return "[text=${safeText}]"
}
}