Signal-iOS/SignalServiceKit/Util/StreamTransform/Input/NonceHeaderInputStreamTransform.swift

93 lines
3.0 KiB
Swift

//
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public class NonceHeaderInputStreamTransform: StreamTransform, BufferedStreamTransform {
private var buffer = Data()
private var headerLength: Int?
private var hasFinishedReadingHeader: Bool
private var needMoreData: Bool = true
public var hasPendingBytes: Bool { return !needMoreData && (headerLength == nil || buffer.count > headerLength!) }
public func readBufferedData() throws -> Data {
let returnedData = try skipPastMetadataHeader()
buffer = Data()
return returnedData
}
init(source: BackupImportSource) {
switch source {
case .remote:
// Remote backups have the header,
// we have to finish reading it first
self.hasFinishedReadingHeader = false
case .linkNsync:
// Link'N'Sync backups have no header; finish immediately.
self.hasFinishedReadingHeader = true
}
}
public func transform(data: Data) throws -> Data {
if hasFinishedReadingHeader {
return data
}
if data.count > 0 {
needMoreData = false
buffer.append(data)
}
return try skipPastMetadataHeader()
}
/// Decode the next chunk of data, if enough data is present in the buffer.
private func skipPastMetadataHeader() throws -> Data {
guard buffer.count > BackupNonce.magicFileSignature.count else {
// We dont have enough data to decode the signature, return for now.
needMoreData = true
return Data()
}
// decode the next variable length int
let (dataSize, intLength) = ChunkedInputStreamTransform.decodeVariableLengthInteger(
buffer: buffer,
start: BackupNonce.magicFileSignature.count
)
guard dataSize > 0 else {
needMoreData = true
// Don't have enough data to decode an int, so return for now
return Data()
}
guard let intDataSize = Int(exactly: dataSize) else {
// The decoded integer is to large to fit into an Int
// The Data operations all require Int Ranges, so
owsFailDebug("Decoded data size too large")
return Data()
}
// Only advance if there is enough data present to both
// decode the variable length integer and skip past the specified
// number of bytes.
let endOfHeader = BackupNonce.magicFileSignature.count + intDataSize + intLength
guard buffer.count >= endOfHeader else {
needMoreData = true
return Data()
}
// Return any data past the header, skipping the header portion.
let returnBuffer = buffer.dropFirst(endOfHeader)
headerLength = endOfHeader
hasFinishedReadingHeader = true
needMoreData = false
buffer = Data()
return returnBuffer
}
}