/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. All rights reserved.
*/
package zip4cj.util
public class Zip4cjUtil {
private init(){}
private static let DOSTIME_BEFORE_1980: Int64 = (1 << 21) | (1 << 16)
private static let MAX_RAW_READ_FULLY_RETRY_ATTEMPTS: Int32 = 15
public static func isStringNullOrEmpty(str: ?String): Bool {
return str.isNone() || str.getOrThrow().trim().size == 0 || str.getOrThrow().trim() == "\0"
}
public static func isStringNotNullAndNotEmpty(str: ?String): Bool {
return str != None && str.getOrThrow().trim().size > 0
}
public static func createDirectoryIfNotExists(file: Path): Bool {
/* Temporal and Temporal Implementation */
if (exists(file)) {
if (!FileInfo(file).isDirectory()) {
throw ZipException("${file} output directory is not valid")
}
} else {
Directory.create(file, recursive: true)
}
return true
}
public static func epochToExtendedDosTime(time: Int64): Int64 {
if (time < 0) {
return DOSTIME_BEFORE_1980
}
let FileTime = DateTime.ofEpoch(second: time / 1000000000, nanosecond: 0)
return (FileTime.year - 1980) << 25 |
FileTime.month.value() << 21 |
(FileTime.dayOfYear + 1) << 16 |
(FileTime.hour * 2) << 11 |
FileTime.minute << 5 |
FileTime.second >> 1
}
private static func dosToEpochTime(dosTime: Int64): Int64 {
let sec: Int64 = Int64(((dosTime << 1) & 0x3e))
let min: Int64 = Int64((dosTime >> 5) & 0x3f)
let hrs: Int64 = Int64(((dosTime >> 11) & 0x1f))
let day: Int64 = Int64(((dosTime >> 16) & 0x1f))
let mon: Int64 = Int64((((dosTime >> 21) & 0xf) - 1))
let year: Int64 = Int64((((dosTime >> 25) & 0x7f) + 1980))
var date: DateTime = DateTime.of(year: year, month: Month.of(mon), dayOfMonth: day, hour: hrs, minute: min,
second: sec, nanosecond: 0)
return date.toUnixTimeStamp().toMilliseconds()
}
public static func convertCharArrayToByteArray(charArray: Array<Rune>, useUtf8Charset: Bool): Array<Byte> {
return if (useUtf8Charset) {
convertCharArrayToByteArrayUsingUtf8(charArray)
} else {
convertCharArrayToByteArrayUsingDefaultCharset(charArray)
}
}
public static func getCompressionMethod(localFileHeader: AbstractFileHeader): CompressionMethod {
if (localFileHeader.getCompressionMethod().getOrThrow().getCode() !=
CompressionMethod.AES_INTERNAL_ONLY.getCode()) {
return localFileHeader.getCompressionMethod().getOrThrow()
}
match (localFileHeader.getAesExtraDataRecord()) {
case None => throw ZipException("AesExtraDataRecord not present in local header for aes encrypted data")
case Some(v) => return v.getCompressionMethod()
}
}
public static func readFully(inputStream: InputStream, bufferToReadInto: Array<Byte>): Int64 {
if (bufferToReadInto.size == 0) {
return 0
}
var readLen: Int64 = inputStream.read(bufferToReadInto)
if (readLen == 0) {
throw ZipIOException("Unexpected EOF reached when trying to read stream")
}
if (Int64(readLen) != bufferToReadInto.size) {
readLen = readUntilBufferIsFull(inputStream, bufferToReadInto, readLen)
if (Int64(readLen) != bufferToReadInto.size) {
throw ZipIOException("Cannot read fully into byte buffer")
}
}
return readLen
}
public static func readFully(inputStream: InputStream, b: Array<Byte>, offset: Int64, length: Int64): Int64 {
if (offset < 0) {
throw IllegalArgumentException("Negative offset")
}
if (length < 0) {
throw IllegalArgumentException("Negative length")
}
if (length == 0) {
return 0
}
if (offset + length > b.size) {
throw IllegalArgumentException("Length greater than buffer size")
}
var numberOfBytesRead = 0
while (numberOfBytesRead != length) {
let currentReadLength = inputStream.read(b[offset + numberOfBytesRead..offset + length])
if (currentReadLength <= 0) {
if (numberOfBytesRead <= 0) {
return -1
}
return numberOfBytesRead
}
numberOfBytesRead += currentReadLength
}
return numberOfBytesRead
}
private static func readUntilBufferIsFull(inputStream: InputStream, bufferToReadInto: Array<Byte>, readLength: Int64): Int64 {
if (readLength < 0) {
throw ZipIOException("Invalid readLength")
}
if (readLength == 0) {
return 0
}
var remainingLength: Int64 = bufferToReadInto.size - readLength
var loopReadLength: Int64 = 0
// first attempt is already done before this method is called
var retryAttempt: Int32 = 1
var readLengthNew: Int64 = readLength
while (readLengthNew < bufferToReadInto.size && loopReadLength != -1 && retryAttempt <
MAX_RAW_READ_FULLY_RETRY_ATTEMPTS) {
loopReadLength = inputStream.read(
bufferToReadInto[Int64(readLengthNew)..Int64(readLengthNew) + Int64(remainingLength)])
if (loopReadLength > 0) {
readLengthNew += loopReadLength
remainingLength -= loopReadLength
}
retryAttempt++
}
return readLengthNew
}
private static func convertCharArrayToByteArrayUsingUtf8(charArray: Array<Rune>): Array<Byte> {
try {
return String(charArray).toArray()
} catch (e: Exception) {
return convertCharArrayToByteArrayUsingDefaultCharset(charArray)
}
}
@OverflowWrapping
private static func convertCharArrayToByteArrayUsingDefaultCharset(charArray: Array<Rune>): Array<Byte> {
var bytes: Array<Byte> = Array<Byte>(charArray.size, repeat: 0)
for (i in 0..charArray.size) {
bytes[i] = UInt8(UInt32(charArray[i]))
}
return bytes
}
}