90 lines
2.6 KiB
Swift
90 lines
2.6 KiB
Swift
import Foundation
|
|
|
|
enum BinaryMeta {
|
|
enum MetaError: Error {
|
|
case invalidBuffer
|
|
case invalidMeta
|
|
}
|
|
|
|
struct FileHeaderMeta: Codable {
|
|
var filename: String?
|
|
var contentType: String?
|
|
}
|
|
|
|
static func mergeDataArrays(_ arrays: [Data]) -> Data {
|
|
var totalLength = 0
|
|
for arr in arrays {
|
|
totalLength += 4 + arr.count
|
|
}
|
|
|
|
var merged = Data(capacity: totalLength)
|
|
for arr in arrays {
|
|
let length = UInt32(arr.count)
|
|
var bytes: [UInt8] = [
|
|
UInt8((length >> 24) & 0xFF),
|
|
UInt8((length >> 16) & 0xFF),
|
|
UInt8((length >> 8) & 0xFF),
|
|
UInt8(length & 0xFF),
|
|
]
|
|
merged.append(contentsOf: bytes)
|
|
merged.append(arr)
|
|
}
|
|
|
|
return merged
|
|
}
|
|
|
|
static func splitDataArrays(_ merged: Data) throws -> [Data] {
|
|
var arrays: [Data] = []
|
|
var offset = 0
|
|
|
|
while offset < merged.count {
|
|
guard offset + 4 <= merged.count else {
|
|
throw MetaError.invalidBuffer
|
|
}
|
|
|
|
let length = merged.withUnsafeBytes { ptr -> Int in
|
|
let b0 = Int(ptr[offset]) << 24
|
|
let b1 = Int(ptr[offset + 1]) << 16
|
|
let b2 = Int(ptr[offset + 2]) << 8
|
|
let b3 = Int(ptr[offset + 3])
|
|
return b0 | b1 | b2 | b3
|
|
}
|
|
offset += 4
|
|
|
|
guard offset + length <= merged.count else {
|
|
throw MetaError.invalidBuffer
|
|
}
|
|
|
|
let arr = merged.subdata(in: offset ..< offset + length)
|
|
arrays.append(arr)
|
|
offset += length
|
|
}
|
|
|
|
return arrays
|
|
}
|
|
|
|
static func appendFileHeaderMeta(data: Data, filename: String?, contentType: String) -> Data {
|
|
let meta = FileHeaderMeta(filename: filename, contentType: contentType)
|
|
guard let metaData = try? JSONEncoder().encode(meta) else {
|
|
return data
|
|
}
|
|
return mergeDataArrays([metaData, data])
|
|
}
|
|
|
|
static func readFileHeaderMeta(buffer: Data) throws -> (data: Data, filename: String?, contentType: String) {
|
|
let arrays = try splitDataArrays(buffer)
|
|
guard arrays.count == 2 else {
|
|
throw MetaError.invalidBuffer
|
|
}
|
|
|
|
let metaData = arrays[0]
|
|
let data = arrays[1]
|
|
|
|
guard let meta = try? JSONDecoder().decode(FileHeaderMeta.self, from: metaData) else {
|
|
throw MetaError.invalidMeta
|
|
}
|
|
|
|
return (data, meta.filename, meta.contentType ?? "application/octet-stream")
|
|
}
|
|
}
|