import Foundation import Postbox public enum LocalizationEntry: Equatable { case string(key: String, value: String) case pluralizedString(key: String, zero: String?, one: String?, two: String?, few: String?, many: String?, other: String) public var key: String { switch self { case let .string(key, _): return key case let .pluralizedString(key, _, _, _, _, _, _): return key } } } private struct LocalizationEntryFlags: OptionSet { var rawValue: Int8 init(rawValue: Int8) { self.rawValue = rawValue } init() { self.rawValue = 0 } static let pluralized = LocalizationEntryFlags(rawValue: (1 << 0)) static let hasZero = LocalizationEntryFlags(rawValue: (1 << 1)) static let hasOne = LocalizationEntryFlags(rawValue: (1 << 2)) static let hasTwo = LocalizationEntryFlags(rawValue: (1 << 3)) static let hasFew = LocalizationEntryFlags(rawValue: (1 << 4)) static let hasMany = LocalizationEntryFlags(rawValue: (1 << 5)) } private func writeString(_ buffer: WriteBuffer, _ string: String) { if let data = string.data(using: .utf8) { var length: Int32 = Int32(data.count) buffer.write(&length, offset: 0, length: 4) buffer.write(data) } else { var length: Int32 = 0 buffer.write(&length, offset: 0, length: 4) } } public final class Localization: PostboxCoding, Equatable { public let version: Int32 public let entries: [LocalizationEntry] public init(version: Int32, entries: [LocalizationEntry]) { self.version = version self.entries = entries } public init(decoder: PostboxDecoder) { self.version = decoder.decodeInt32ForKey("v", orElse: 0) let count = decoder.decodeInt32ForKey("c", orElse: 0) var entries: [LocalizationEntry] = [] if let data = decoder.decodeBytesForKey("d") { for _ in 0 ..< count { var flagsValue: Int8 = 0 data.read(&flagsValue, offset: 0, length: 1) let flags = LocalizationEntryFlags(rawValue: flagsValue) var length: Int32 = 0 data.read(&length, offset: 0, length: 4) let keyData = Data(bytes: data.memory.advanced(by: data.offset), count: Int(length)) let key = String(data: keyData, encoding: .utf8) data.skip(Int(length)) if flags.contains(.pluralized) { var zero: String? var one: String? var two: String? var few: String? var many: String? var other: String? if flags.contains(.hasZero) { length = 0 data.read(&length, offset: 0, length: 4) let valueData = Data(bytes: data.memory.advanced(by: data.offset), count: Int(length)) let value = String(data: valueData, encoding: .utf8) data.skip(Int(length)) zero = value } if flags.contains(.hasOne) { length = 0 data.read(&length, offset: 0, length: 4) let valueData = Data(bytes: data.memory.advanced(by: data.offset), count: Int(length)) let value = String(data: valueData, encoding: .utf8) data.skip(Int(length)) one = value } if flags.contains(.hasTwo) { length = 0 data.read(&length, offset: 0, length: 4) let valueData = Data(bytes: data.memory.advanced(by: data.offset), count: Int(length)) let value = String(data: valueData, encoding: .utf8) data.skip(Int(length)) two = value } if flags.contains(.hasFew) { length = 0 data.read(&length, offset: 0, length: 4) let valueData = Data(bytes: data.memory.advanced(by: data.offset), count: Int(length)) let value = String(data: valueData, encoding: .utf8) data.skip(Int(length)) few = value } if flags.contains(.hasMany) { length = 0 data.read(&length, offset: 0, length: 4) let valueData = Data(bytes: data.memory.advanced(by: data.offset), count: Int(length)) let value = String(data: valueData, encoding: .utf8) data.skip(Int(length)) many = value } length = 0 data.read(&length, offset: 0, length: 4) let valueData = Data(bytes: data.memory.advanced(by: data.offset), count: Int(length)) let value = String(data: valueData, encoding: .utf8) data.skip(Int(length)) other = value if let key = key, let other = other { entries.append(.pluralizedString(key: key, zero: zero, one: one, two: two, few: few, many: many, other: other)) } } else { length = 0 data.read(&length, offset: 0, length: 4) let valueData = Data(bytes: data.memory.advanced(by: data.offset), count: Int(length)) let value = String(data: valueData, encoding: .utf8) data.skip(Int(length)) if let key = key, let value = value { entries.append(.string(key: key, value: value)) } } } } self.entries = entries } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt32(self.version, forKey: "v") encoder.encodeInt32(Int32(self.entries.count), forKey: "c") let buffer = WriteBuffer() for entry in self.entries { var flags: LocalizationEntryFlags = [] switch entry { case .string: flags = [] case let .pluralizedString(_, zero, one, two, few, many, _): flags.insert(.pluralized) if zero != nil { flags.insert(.hasZero) } if one != nil { flags.insert(.hasOne) } if two != nil { flags.insert(.hasTwo) } if few != nil { flags.insert(.hasFew) } if many != nil { flags.insert(.hasMany) } } var flagsValue: Int8 = flags.rawValue buffer.write(&flagsValue, offset: 0, length: 1) switch entry { case let .string(key, value): writeString(buffer, key) writeString(buffer, value) case let .pluralizedString(key, zero, one, two, few, many, other): writeString(buffer, key) if let zero = zero { writeString(buffer, zero) } if let one = one { writeString(buffer, one) } if let two = two { writeString(buffer, two) } if let few = few { writeString(buffer, few) } if let many = many { writeString(buffer, many) } writeString(buffer, other) } } encoder.encodeBytes(buffer, forKey: "d") } public static func ==(lhs: Localization, rhs: Localization) -> Bool { if lhs === rhs { return true } if lhs.entries == rhs.entries { return true } return false } }