Skip to content

Instantly share code, notes, and snippets.

@VaslD
Last active August 25, 2023 05:07
Show Gist options
  • Save VaslD/cf5efa1380e16c480a2f335dad72c558 to your computer and use it in GitHub Desktop.
Save VaslD/cf5efa1380e16c480a2f335dad72c558 to your computer and use it in GitHub Desktop.
import Foundation
/// 四字符码,也称四字代码、FCC、4CC,大量运用于文件格式和经典 Mac OS API 中。
///
/// Darwin 提供 `FourCharCode` 数据类型用于保存四字符码,但 `FourCharCode` 只是 32 位整数 (`UInt32`, `CUnsignedInt`)
/// 的类型别名。在编程中,四字符码通常以字符串(例如:`"avc1"`)而非整数(例如:`1635148593`)表示,因此
/// `FourCharCode` 难以使用;也由于 `FourCharCode` 并非独立类型,向其添加扩展将导致扩展方法在所有整数上可用,极易引起误解且污染
/// Swift 标准类型 API。
///
/// ``FourCC`` 声明了新的类型,提供对 `FourCharCode` 的封装。``FourCC`` 可在源代码中通过字符串构造、或在运行时通过 `FourCharCode`
/// 构造,两种用法均保证无损转换;访问 ``rawValue`` 可获得 `FourCharCode`;将其传递给 `String` 构造方法可获得 4 个 ASCII
/// 字符的字符串。``FourCC`` 也支持在运行时通过字符串变量构造,但可能失败。
///
/// ```swift
/// print("avc1" as FourCC) // avc1
///
/// print(FourCC("avc1").rawValue) // 1635148593
///
/// let code = "avc1"
/// print(FourCC(code)?.rawValue) // Optional(1635148593)
/// ```
///
/// 得益于 Swift 结构体使用通用内存布局 (Universal),``FourCC`` 的内存表达与 `FourCharCode` 完全相同。尽管不推荐,但必要时
/// ``FourCC`` 可被强制转换为 `FourCharCode` 使用。
///
/// - Warning: 请勿将 `FourCharCode` 强制转换为 ``FourCC`` 使用。在当前版本 Swift 中,此转换会成功;但 32 位整数 (`UInt32`)
/// 是标准类型,将其转换为自定义类型存在比将自定义类型转换为整数更大的类型安全风险。请使用无损构造方法 ``init(_:)``。
///
/// ```swift
/// print(withUnsafeBytes(of: FourCC("avc1")) {
/// "0x" + $0.map {
/// String(format: "%02X", $0)
/// }.joined()
/// }) // 0x31637661
///
/// print(withUnsafeBytes(of: FourCC("avc1")) {
/// $0.withMemoryRebound(to: FourCharCode.self) {
/// $0.baseAddress!.pointee
/// }
/// }) // 1635148593
/// ```
@frozen
public struct FourCC: RawRepresentable, LosslessStringConvertible, ExpressibleByStringLiteral {
public let rawValue: FourCharCode
public init(_ code: FourCharCode) {
self.rawValue = code
}
public init(rawValue: FourCharCode) {
self.rawValue = rawValue
}
public var description: String {
withUnsafeBytes(of: self.rawValue.bigEndian) {
String(bytes: $0, encoding: .ascii)!
}
}
public init?(_ description: String) {
guard description.utf8.count == 4 else { return nil }
self.rawValue = Data(description.utf8).withUnsafeBytes {
$0.loadUnaligned(as: FourCharCode.self).byteSwapped
}
}
public init(stringLiteral value: StaticString) {
precondition(value.isASCII, "FourCC string must be ASCII.")
self.rawValue = value.withUTF8Buffer {
precondition($0.count == 4, "FourCC string must be 4-character long.")
return UnsafeRawBufferPointer($0).loadUnaligned(as: FourCharCode.self).bigEndian
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment