// // Data+Serialization.swift // ZIPFoundation // // Copyright © 2017-2020 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors. // Released under the MIT License. // // See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information. // import Foundation protocol DataSerializable { static var size: Int { get } init?(data: Data, additionalDataProvider: (Int) throws -> Data) var data: Data { get } } extension Data { enum DataError: Error { case unreadableFile case unwritableFile } func scanValue(start: Int) -> T { let subdata = self.subdata(in: start...size) #if swift(>=5.0) return subdata.withUnsafeBytes { $0.load(as: T.self) } #else return subdata.withUnsafeBytes { $0.pointee } #endif } static func readStruct(from file: UnsafeMutablePointer, at offset: Int) -> T? where T: DataSerializable { fseek(file, offset, SEEK_SET) guard let data = try? self.readChunk(of: T.size, from: file) else { return nil } let structure = T(data: data, additionalDataProvider: { (additionalDataSize) -> Data in return try self.readChunk(of: additionalDataSize, from: file) }) return structure } static func consumePart(of size: Int, chunkSize: Int, skipCRC32: Bool = false, provider: Provider, consumer: Consumer) throws -> CRC32 { var checksum = CRC32(0) guard size > 0 else { try consumer(Data()) return checksum } let readInOneChunk = (size < chunkSize) var chunkSize = readInOneChunk ? size : chunkSize var bytesRead = 0 while bytesRead < size { let remainingSize = size - bytesRead chunkSize = remainingSize < chunkSize ? remainingSize : chunkSize let data = try provider(bytesRead, chunkSize) try consumer(data) if !skipCRC32 { checksum = data.crc32(checksum: checksum) } bytesRead += chunkSize } return checksum } static func readChunk(of size: Int, from file: UnsafeMutablePointer) throws -> Data { let alignment = MemoryLayout.alignment #if swift(>=4.1) let bytes = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment) #else let bytes = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: alignment) #endif let bytesRead = fread(bytes, 1, size, file) let error = ferror(file) if error > 0 { throw DataError.unreadableFile } #if swift(>=4.1) return Data(bytesNoCopy: bytes, count: bytesRead, deallocator: .custom({ buf, _ in buf.deallocate() })) #else let deallocator = Deallocator.custom({ buf, _ in buf.deallocate(bytes: size, alignedTo: 1) }) return Data(bytesNoCopy: bytes, count: bytesRead, deallocator: deallocator) #endif } static func write(chunk: Data, to file: UnsafeMutablePointer) throws -> Int { var sizeWritten = 0 chunk.withUnsafeBytes { (rawBufferPointer) in if let baseAddress = rawBufferPointer.baseAddress, rawBufferPointer.count > 0 { let pointer = baseAddress.assumingMemoryBound(to: UInt8.self) sizeWritten = fwrite(pointer, 1, chunk.count, file) } } let error = ferror(file) if error > 0 { throw DataError.unwritableFile } return sizeWritten } }