// // Copyright 2024 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation public class ChunkedInputStreamTransform: StreamTransform { private var buffer = Data() public var hasPendingBytes: Bool { return buffer.count > 0 } public func readBufferedData() throws -> Data { try getNextChunk() } public func transform(data: Data) throws -> Data { buffer.append(data) return try getNextChunk() } /// Decode the next chunk of data, if enough data is present in the buffer. private func getNextChunk() throws -> Data { // decode the next variable length int let (dataSize, intLength) = decodeVariableLengthInteger(buffer) guard dataSize > 0 else { // 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 read the specified // number of bytes. let endOfBuffer = intDataSize + intLength guard buffer.count >= endOfBuffer else { return Data() } // Return a chunk of data from the buffer and advence the buffer. let returnBuffer = buffer.subdata(in: intLength..= buffer.count { buffer = Data() } else { buffer = buffer.subdata(in: endOfBuffer.. (result: UInt64, length: Int) { guard data.count > 0 else { return (result: 0, length: 0) } return data.withUnsafeBytes { guard let baseAddress = $0.bindMemory(to: UInt8.self).baseAddress else { return (result: 0, length: 0) } var start = baseAddress var length = data.count var count = 1 var c = start[0] start += 1 length -= 1 if c & 0x80 == 0 { return (result: UInt64(c), length: count) } var value = UInt64(c & 0x7f) var shift = UInt64(7) while true { if length < 1 || shift > 63 { return (result: 0, length: 0) } c = start[0] start += 1 length -= 1 count += 1 value |= UInt64(c & 0x7f) << shift if c & 0x80 == 0 { return (result: value, length: count) } shift += 7 } } } }