// Convertible.swift
// This file is part of KeePass.swift
//
// Copyright © 2021 Maxime Epain. All rights reserved.
//
// KeePass.swift is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// KeePass.swift is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with KeePass.swift. If not, see .
import Foundation
/// A type with a customized bytes representation.
public protocol CustomBytesConvertible {
/// A bytes representation of this instance.
var bytes: Bytes { get }
}
public protocol LosslessBytesConvertible: CustomBytesConvertible {
/// Instantiates an instance of the conforming type from bytes
/// representation.
init(_ bytes: Bytes) throws
}
// MARK: - Streamable Boolean Bytes
extension Bool: LosslessBytesConvertible {
public init(_ bytes: Bytes) throws {
guard bytes.lenght == MemoryLayout.size else { throw BinaryError.invalidLenght }
self = bytes.withUnsafeBytes { $0.load(as: Self.self) }
}
public var bytes: Bytes {
withUnsafeBytes(of: self) { Bytes($0) }
}
}
// MARK: - Streamable Integer
extension LosslessBytesConvertible where Self: BinaryInteger {
public init(_ bytes: Bytes) throws {
guard bytes.lenght == MemoryLayout.size else { throw BinaryError.invalidLenght }
self = bytes.withUnsafeBytes { $0.load(as: Self.self) }
}
public var bytes: Bytes {
withUnsafeBytes(of: self) { Bytes($0) }
}
}
extension Int: LosslessBytesConvertible {}
extension Int8: LosslessBytesConvertible {}
extension Int16: LosslessBytesConvertible {}
extension Int32: LosslessBytesConvertible {}
extension Int64: LosslessBytesConvertible {}
extension UInt: LosslessBytesConvertible {}
extension UInt8: LosslessBytesConvertible {}
extension UInt16: LosslessBytesConvertible {}
extension UInt32: LosslessBytesConvertible {}
extension UInt64: LosslessBytesConvertible {}
// MARK: - Streamable Floating Point
extension LosslessBytesConvertible where Self: FloatingPoint {
public init(_ bytes: Bytes) throws {
guard bytes.lenght == MemoryLayout.size else { throw BinaryError.invalidLenght }
self = bytes.withUnsafeBytes { $0.load(as: Self.self) }
}
public var bytes: Bytes {
withUnsafeBytes(of: self) { Bytes($0) }
}
}
extension Double: LosslessBytesConvertible {}
extension Float: LosslessBytesConvertible {}
// MARK: - RawRepresentable Bytes
extension LosslessBytesConvertible where Self: RawRepresentable, RawValue: LosslessBytesConvertible {
public init(_ bytes: Bytes) throws {
let rawValue = try RawValue(bytes)
guard let value = Self(rawValue: rawValue) else { throw BinaryError.invalidValue }
self = value
}
public var bytes: Bytes { rawValue.bytes }
}
// MARK: - Bytes
extension Bytes: LosslessBytesConvertible {
public init(_ bytes: Bytes) throws {
self = bytes
}
public var bytes: Bytes { self }
}
// MARK: - Bytes Array
extension Array where Element == Bytes {
subscript(_ index: Int) -> T? where T: LosslessBytesConvertible {
try? T(self[index])
}
}
// MARK: - Bytes Dictionary
extension Dictionary where Value == Bytes {
subscript(_ key: Key) -> T? where T: LosslessBytesConvertible {
guard let bytes = self[key] else { return nil }
return try? T(bytes)
}
}
// MARK: - String Bytes
extension String: LosslessBytesConvertible {
public var bytes: Bytes { bytes(using: .utf8) ?? [] }
public init(_ bytes: Bytes) throws {
guard let string = String(bytes: bytes, encoding: .utf8) else { throw BinaryError.invalidValue }
self = string
}
}
// MARK: - Data Bytes
extension Data: LosslessBytesConvertible {
public var bytes: Bytes { Bytes(data: self) }
public init(_ bytes: Bytes) throws {
self = Data(bytes.rawValue)
}
}
// MARK: - UUID Bytes
extension UUID: LosslessBytesConvertible {
public var bytes: Bytes {
withUnsafeBytes(of: uuid) { Bytes($0) }
}
public init(_ bytes: Bytes) throws {
guard bytes.lenght == MemoryLayout.size else { throw BinaryError.invalidLenght }
let uuid = bytes.withUnsafeBytes { $0.load(as: uuid_t.self) }
self = UUID(uuid: uuid)
}
}
extension Optional where Wrapped: LosslessBytesConvertible {
public init(_ bytes: Bytes?) {
if let bytes = bytes, let wrapped = try? Wrapped(bytes) {
self = .some(wrapped)
} else {
self = .none
}
}
}