327 lines
9.1 KiB
Swift
327 lines
9.1 KiB
Swift
import Foundation
|
|
|
|
protocol ByteArrayConvertible {
|
|
|
|
var data:Data { get }
|
|
var length:Int { get set }
|
|
var position:Int { get set }
|
|
var bytesAvailable:Int { get }
|
|
subscript(i: Int) -> UInt8 { get set }
|
|
|
|
@discardableResult
|
|
func writeUInt8(_ value:UInt8) -> Self
|
|
func readUInt8() throws -> UInt8
|
|
|
|
@discardableResult
|
|
func writeInt8(_ value:Int8) -> Self
|
|
func readInt8() throws -> Int8
|
|
|
|
@discardableResult
|
|
func writeUInt16(_ value:UInt16) -> Self
|
|
func readUInt16() throws -> UInt16
|
|
|
|
@discardableResult
|
|
func writeInt16(_ value:Int16) -> Self
|
|
func readInt16() throws -> Int16
|
|
|
|
@discardableResult
|
|
func writeUInt24(_ value:UInt32) -> Self
|
|
func readUInt24() throws -> UInt32
|
|
|
|
@discardableResult
|
|
func writeUInt32(_ value:UInt32) -> Self
|
|
func readUInt32() throws -> UInt32
|
|
|
|
@discardableResult
|
|
func writeInt32(_ value:Int32) -> Self
|
|
func readInt32() throws -> Int32
|
|
|
|
@discardableResult
|
|
func writeDouble(_ value:Double) -> Self
|
|
func readDouble() throws -> Double
|
|
|
|
@discardableResult
|
|
func writeFloat(_ value:Float) -> Self
|
|
func readFloat() throws -> Float
|
|
|
|
@discardableResult
|
|
func writeUTF8(_ value:String) throws -> Self
|
|
func readUTF8() throws -> String
|
|
|
|
@discardableResult
|
|
func writeUTF8Bytes(_ value:String) -> Self
|
|
func readUTF8Bytes(_ length:Int) throws -> String
|
|
|
|
@discardableResult
|
|
func writeBytes(_ value:Data) -> Self
|
|
func readBytes(_ length:Int) throws -> Data
|
|
|
|
@discardableResult
|
|
func clear() -> Self
|
|
}
|
|
|
|
// MARK: -
|
|
open class ByteArray: ByteArrayConvertible {
|
|
static let fillZero:[UInt8] = [0x00]
|
|
|
|
static let sizeOfInt8:Int = 1
|
|
static let sizeOfInt16:Int = 2
|
|
static let sizeOfInt24:Int = 3
|
|
static let sizeOfInt32:Int = 4
|
|
static let sizeOfFloat:Int = 4
|
|
static let sizeOfDouble:Int = 8
|
|
|
|
public enum Error: Swift.Error {
|
|
case eof
|
|
case parse
|
|
}
|
|
|
|
init() {
|
|
}
|
|
|
|
init(data:Data) {
|
|
self.data = data
|
|
}
|
|
|
|
fileprivate(set) var data:Data = Data()
|
|
|
|
open var length:Int {
|
|
get {
|
|
return data.count
|
|
}
|
|
set {
|
|
switch true {
|
|
case (data.count < newValue):
|
|
data.append(Data.init(count: newValue - data.count))
|
|
case (newValue < data.count):
|
|
data = data.subdata(in: 0..<newValue)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
open var position:Int = 0
|
|
|
|
open var bytesAvailable:Int {
|
|
return data.count - position
|
|
}
|
|
|
|
open subscript(i: Int) -> UInt8 {
|
|
get {
|
|
return data[i]
|
|
}
|
|
set {
|
|
data[i] = newValue
|
|
}
|
|
}
|
|
|
|
open func readUInt8() throws -> UInt8 {
|
|
guard ByteArray.sizeOfInt8 <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
defer {
|
|
position += 1
|
|
}
|
|
return data[position]
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeUInt8(_ value:UInt8) -> Self {
|
|
return writeBytes(value.data)
|
|
}
|
|
|
|
open func readInt8() throws -> Int8 {
|
|
guard ByteArray.sizeOfInt8 <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
defer {
|
|
position += 1
|
|
}
|
|
return Int8(bitPattern: UInt8(data[position]))
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeInt8(_ value:Int8) -> Self {
|
|
return writeBytes(UInt8(bitPattern: value).data)
|
|
}
|
|
|
|
open func readUInt16() throws -> UInt16 {
|
|
guard ByteArray.sizeOfInt16 <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += ByteArray.sizeOfInt16
|
|
return UInt16(data: data[position - ByteArray.sizeOfInt16..<position]).bigEndian
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeUInt16(_ value:UInt16) -> Self {
|
|
return writeBytes(value.bigEndian.data)
|
|
}
|
|
|
|
open func readInt16() throws -> Int16 {
|
|
guard ByteArray.sizeOfInt16 <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += ByteArray.sizeOfInt16
|
|
return Int16(data: data[position - ByteArray.sizeOfInt16..<position]).bigEndian
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeInt16(_ value:Int16) -> Self {
|
|
return writeBytes(value.bigEndian.data)
|
|
}
|
|
|
|
open func readUInt24() throws -> UInt32 {
|
|
guard ByteArray.sizeOfInt24 <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += ByteArray.sizeOfInt24
|
|
return UInt32(data: ByteArray.fillZero + data[position - ByteArray.sizeOfInt24..<position]).bigEndian
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeUInt24(_ value:UInt32) -> Self {
|
|
return writeBytes(value.bigEndian.data.subdata(in: 1..<ByteArray.sizeOfInt24 + 1))
|
|
}
|
|
|
|
open func readUInt32() throws -> UInt32 {
|
|
guard ByteArray.sizeOfInt32 <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += ByteArray.sizeOfInt32
|
|
return UInt32(data: data[position - ByteArray.sizeOfInt32..<position]).bigEndian
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeUInt32(_ value:UInt32) -> Self {
|
|
return writeBytes(value.bigEndian.data)
|
|
}
|
|
|
|
open func readInt32() throws -> Int32 {
|
|
guard ByteArray.sizeOfInt32 <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += ByteArray.sizeOfInt32
|
|
return Int32(data: data[position - ByteArray.sizeOfInt32..<position]).bigEndian
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeInt32(_ value:Int32) -> Self {
|
|
return writeBytes(value.bigEndian.data)
|
|
}
|
|
|
|
open func readDouble() throws -> Double {
|
|
guard ByteArray.sizeOfDouble <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += ByteArray.sizeOfDouble
|
|
return Double(data: Data(data.subdata(in: position - ByteArray.sizeOfDouble..<position).reversed()))
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeDouble(_ value:Double) -> Self {
|
|
return writeBytes(Data(value.data.reversed()))
|
|
}
|
|
|
|
open func readFloat() throws -> Float {
|
|
guard ByteArray.sizeOfFloat <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += ByteArray.sizeOfFloat
|
|
return Float(data: Data(data.subdata(in: position - ByteArray.sizeOfFloat..<position).reversed()))
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeFloat(_ value:Float) -> Self {
|
|
return writeBytes(Data(value.data.reversed()))
|
|
}
|
|
|
|
open func readUTF8() throws -> String {
|
|
return try readUTF8Bytes(Int(try readUInt16()))
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeUTF8(_ value:String) throws -> Self {
|
|
let utf8:Data = Data(value.utf8)
|
|
return writeUInt16(UInt16(utf8.count)).writeBytes(utf8)
|
|
}
|
|
|
|
open func readUTF8Bytes(_ length:Int) throws -> String {
|
|
guard length <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += length
|
|
|
|
guard let result:String = String(data: data.subdata(in: position - length..<position), encoding: .utf8) else {
|
|
throw ByteArray.Error.parse
|
|
}
|
|
return result
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeUTF8Bytes(_ value:String) -> Self {
|
|
return writeBytes(Data(value.utf8))
|
|
}
|
|
|
|
open func readBytes(_ length:Int) throws -> Data {
|
|
guard length <= bytesAvailable else {
|
|
throw ByteArray.Error.eof
|
|
}
|
|
position += length
|
|
return data.subdata(in: position - length..<position)
|
|
}
|
|
|
|
@discardableResult
|
|
open func writeBytes(_ value:Data) -> Self {
|
|
if (position == data.count) {
|
|
data.append(value)
|
|
position = data.count
|
|
return self
|
|
}
|
|
let length:Int = min(data.count, value.count)
|
|
data[position..<position + length] = value[0..<length]
|
|
if (length == data.count) {
|
|
data.append(value[length..<value.count])
|
|
}
|
|
position += value.count
|
|
return self
|
|
}
|
|
|
|
@discardableResult
|
|
open func clear() -> Self {
|
|
position = 0
|
|
data.removeAll()
|
|
return self
|
|
}
|
|
|
|
func sequence(_ length:Int, lambda:((ByteArray) -> Void)) {
|
|
let r:Int = (data.count - position) % length
|
|
for index in stride(from: data.startIndex.advanced(by: position), to: data.endIndex.advanced(by: -r), by: length) {
|
|
lambda(ByteArray(data: data.subdata(in: index..<index.advanced(by: length))))
|
|
}
|
|
if (0 < r) {
|
|
lambda(ByteArray(data: data.advanced(by: data.endIndex - r)))
|
|
}
|
|
}
|
|
|
|
func toUInt32() -> [UInt32] {
|
|
let size:Int = MemoryLayout<UInt32>.size
|
|
if ((data.endIndex - position) % size != 0) {
|
|
return []
|
|
}
|
|
var result:[UInt32] = []
|
|
for index in stride(from: data.startIndex.advanced(by: position), to: data.endIndex, by: size) {
|
|
result.append(UInt32(data: data[index..<index.advanced(by: size)]))
|
|
}
|
|
return result
|
|
}
|
|
}
|
|
|
|
extension ByteArray: CustomStringConvertible {
|
|
// MARK: CustomStringConvertible
|
|
public var description:String {
|
|
return Mirror(reflecting: self).description
|
|
}
|
|
}
|