Files
2026-01-31 15:30:24 +08:00

62 lines
2.0 KiB
Swift

// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
extension Data {
@inline(__always)
mutating func appendAsTarBlock(_ data: Data) {
self.append(data)
let paddingSize = data.count.roundTo512() - data.count
self.append(Data(count: paddingSize))
}
mutating func append(tarInt value: Int?, maxLength: Int) {
guard var value = value else {
// No value; fill field with NULLs.
self.append(Data(count: maxLength))
return
}
let maxOctalValue = (1 << (maxLength * 3)) - 1
guard value > maxOctalValue || value < 0 else {
// Normal octal encoding.
self.append(Data(String(value, radix: 8).utf8).zeroPad(maxLength))
return
}
// Base-256 encoding.
// As long as we have at least 8 bytes for our value, conversion to base-256 will always succeed, since (64-bit)
// Int.max neatly fits into 8 bytes of 256-base encoding.
assert(maxLength >= 8 && Int.bitWidth <= 64)
var buffer = Array(repeating: 0 as UInt8, count: maxLength)
for i in stride(from: maxLength - 1, to: 0, by: -1) {
buffer[i] = UInt8(truncatingIfNeeded: value & 0xFF)
value >>= 8
}
buffer[0] |= 0x80 // Highest bit indicates base-256 encoding.
self.append(Data(buffer))
}
mutating func append(tarString string: String?, maxLength: Int) {
guard let string = string else {
// No value; fill field with NULLs.
self.append(Data(count: maxLength))
return
}
self.append(Data(string.utf8).zeroPad(maxLength))
}
/// This should work in the same way as `String.padding(toLength: length, withPad: "\0", startingAt: 0)`.
@inline(__always)
private func zeroPad(_ length: Int) -> Data {
var out = length < self.count ? self.prefix(upTo: length) : self
out.append(Data(count: length - out.count))
return out
}
}