126 lines
3.5 KiB
Swift
126 lines
3.5 KiB
Swift
//
|
|
// Writer.swift
|
|
// SwresTools
|
|
//
|
|
|
|
import Foundation
|
|
|
|
enum WriterEndianness {
|
|
case littleEndian
|
|
case bigEndian
|
|
}
|
|
|
|
enum WriterError: SwresError {
|
|
case invalidParameter(message: String)
|
|
case alreadyFinalized
|
|
case internalError
|
|
|
|
var description: String {
|
|
switch self {
|
|
case .invalidParameter(let message):
|
|
return "Invalid parameter. \(message)"
|
|
case .alreadyFinalized:
|
|
return "The writer has already been finalized."
|
|
case .internalError:
|
|
return "Internal error."
|
|
}
|
|
}
|
|
}
|
|
|
|
class Writer {
|
|
private let _endianness: WriterEndianness
|
|
private var _outputStream: OutputStream
|
|
private var _finalized: Bool = false
|
|
|
|
init(endianness: WriterEndianness) {
|
|
_endianness = endianness
|
|
_outputStream = OutputStream.toMemory()
|
|
_outputStream.open()
|
|
}
|
|
|
|
private func _unwrapped(_ endianness: WriterEndianness?) -> WriterEndianness {
|
|
if let unwrappedEndianness = endianness {
|
|
return unwrappedEndianness
|
|
}
|
|
return _endianness
|
|
}
|
|
|
|
private func _validateNotFinalized() throws {
|
|
guard !_finalized else {
|
|
throw WriterError.alreadyFinalized
|
|
}
|
|
}
|
|
|
|
func write(_ value: Int16, endianness: WriterEndianness? = nil) throws {
|
|
try _validateNotFinalized()
|
|
|
|
var value = value
|
|
|
|
switch _unwrapped(endianness) {
|
|
case .littleEndian:
|
|
value = value.littleEndian
|
|
case .bigEndian:
|
|
value = value.bigEndian
|
|
}
|
|
|
|
try withUnsafePointer(to: &value) { (valuePointer: UnsafePointer<Int16>) in
|
|
let bytePointer = UnsafeRawPointer(valuePointer).assumingMemoryBound(to: UInt8.self)
|
|
try _write(bytePointer, length: MemoryLayout<UInt16>.size)
|
|
}
|
|
}
|
|
|
|
func write(_ value: Int32, endianness: WriterEndianness? = nil) throws {
|
|
try _validateNotFinalized()
|
|
|
|
var value = value
|
|
|
|
switch _unwrapped(endianness) {
|
|
case .littleEndian:
|
|
value = value.littleEndian
|
|
case .bigEndian:
|
|
value = value.bigEndian
|
|
}
|
|
|
|
try withUnsafePointer(to: &value) { (valuePointer: UnsafePointer<Int32>) in
|
|
let bytePointer = UnsafeRawPointer(valuePointer).assumingMemoryBound(to: UInt8.self)
|
|
try _write(bytePointer, length: MemoryLayout<UInt32>.size)
|
|
}
|
|
}
|
|
|
|
func write(_ data: Data) throws {
|
|
try _validateNotFinalized()
|
|
|
|
_ = try data.withUnsafeBytes { (pointer: UnsafePointer<UInt8>) in
|
|
try _write(pointer, length: data.count)
|
|
}
|
|
}
|
|
|
|
func write(ascii: String) throws {
|
|
try _validateNotFinalized()
|
|
|
|
guard let data = ascii.data(using: String.Encoding.ascii, allowLossyConversion: true) else {
|
|
throw WriterError.invalidParameter(message: "Invalid ASCII string.")
|
|
}
|
|
|
|
try write(data)
|
|
}
|
|
|
|
private func _write(_ pointer: UnsafePointer<UInt8>, length: Int) throws {
|
|
let writtenBytes = _outputStream.write(pointer, maxLength: length)
|
|
guard writtenBytes == length else {
|
|
throw WriterError.internalError
|
|
}
|
|
}
|
|
|
|
func finalize() throws -> Data {
|
|
try _validateNotFinalized()
|
|
|
|
let result = _outputStream.property(forKey: Stream.PropertyKey.dataWrittenToMemoryStreamKey)
|
|
guard let unwrappedResult = result, let data = unwrappedResult as? Data else {
|
|
throw WriterError.internalError
|
|
}
|
|
_finalized = true
|
|
return data
|
|
}
|
|
}
|