mirror of
https://github.com/lux-tel/session-ios.git
synced 2026-10-30 20:38:32 +00:00
# Conflicts: # Podfile # Podfile.lock # Session.xcodeproj/project.pbxproj # Session/Calls/Call Management/SessionCall.swift # Session/Calls/Call Management/SessionCallManager.swift # Session/Calls/CallVC.swift # Session/Conversations/ConversationVC+Interaction.swift # Session/Conversations/ConversationVC.swift # Session/Conversations/ConversationViewModel.swift # Session/Conversations/Message Cells/Content Views/MediaAlbumView.swift # Session/Conversations/Settings/ThreadSettingsViewModel.swift # Session/Emoji/Emoji+Available.swift # Session/Home/GlobalSearch/GlobalSearchViewController.swift # Session/Home/HomeVC.swift # Session/Home/HomeViewModel.swift # Session/Home/New Conversation/NewDMVC.swift # Session/Media Viewing & Editing/DocumentTitleViewController.swift # Session/Media Viewing & Editing/GIFs/GifPickerCell.swift # Session/Media Viewing & Editing/GIFs/GifPickerViewController.swift # Session/Media Viewing & Editing/ImagePickerController.swift # Session/Media Viewing & Editing/MediaTileViewController.swift # Session/Media Viewing & Editing/PhotoCapture.swift # Session/Media Viewing & Editing/PhotoCaptureViewController.swift # Session/Media Viewing & Editing/PhotoLibrary.swift # Session/Media Viewing & Editing/SendMediaNavigationController.swift # Session/Meta/AppDelegate.swift # Session/Meta/AppEnvironment.swift # Session/Meta/MainAppContext.swift # Session/Meta/SessionApp.swift # Session/Notifications/NotificationPresenter.swift # Session/Notifications/PushRegistrationManager.swift # Session/Notifications/SyncPushTokensJob.swift # Session/Notifications/UserNotificationsAdaptee.swift # Session/Onboarding/LandingVC.swift # Session/Onboarding/LinkDeviceVC.swift # Session/Onboarding/Onboarding.swift # Session/Onboarding/RegisterVC.swift # Session/Onboarding/RestoreVC.swift # Session/Settings/HelpViewModel.swift # Session/Settings/NukeDataModal.swift # Session/Shared/FullConversationCell.swift # Session/Shared/OWSBezierPathView.m # Session/Utilities/BackgroundPoller.swift # Session/Utilities/MockDataGenerator.swift # SessionMessagingKit/Configuration.swift # SessionMessagingKit/Crypto/Crypto+SessionMessagingKit.swift # SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift # SessionMessagingKit/Database/Migrations/_014_GenerateInitialUserConfigDumps.swift # SessionMessagingKit/Database/Migrations/_015_BlockCommunityMessageRequests.swift # SessionMessagingKit/Database/Migrations/_018_DisappearingMessagesConfiguration.swift # SessionMessagingKit/Database/Models/Attachment.swift # SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift # SessionMessagingKit/Database/Models/Interaction.swift # SessionMessagingKit/Database/Models/Profile.swift # SessionMessagingKit/Database/Models/SessionThread.swift # SessionMessagingKit/File Server/FileServerAPI.swift # SessionMessagingKit/Jobs/AttachmentDownloadJob.swift # SessionMessagingKit/Jobs/CheckForAppUpdatesJob.swift # SessionMessagingKit/Jobs/DisappearingMessagesJob.swift # SessionMessagingKit/Jobs/FailedMessageSendsJob.swift # SessionMessagingKit/Jobs/MessageSendJob.swift # SessionMessagingKit/Jobs/Types/GroupLeavingJob.swift # SessionMessagingKit/LibSession/Config Handling/LibSession+Contacts.swift # SessionMessagingKit/LibSession/Config Handling/LibSession+ConvoInfoVolatile.swift # SessionMessagingKit/LibSession/Config Handling/LibSession+Shared.swift # SessionMessagingKit/LibSession/Config Handling/LibSession+UserGroups.swift # SessionMessagingKit/LibSession/LibSession+SessionMessagingKit.swift # SessionMessagingKit/Messages/Message.swift # SessionMessagingKit/Open Groups/Crypto/Crypto+OpenGroupAPI.swift # SessionMessagingKit/Open Groups/Models/SOGSMessage.swift # SessionMessagingKit/Open Groups/OpenGroupAPI.swift # SessionMessagingKit/Open Groups/OpenGroupManager.swift # SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift # SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+ExpirationTimers.swift # SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+LegacyClosedGroups.swift # SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+VisibleMessages.swift # SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+LegacyClosedGroups.swift # SessionMessagingKit/Sending & Receiving/MessageReceiver.swift # SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift # SessionMessagingKit/Sending & Receiving/MessageSender.swift # SessionMessagingKit/Sending & Receiving/Notifications/Models/SubscribeRequest.swift # SessionMessagingKit/Sending & Receiving/Notifications/Models/UnsubscribeRequest.swift # SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift # SessionMessagingKit/Sending & Receiving/Pollers/CurrentUserPoller.swift # SessionMessagingKit/Sending & Receiving/Pollers/GroupPoller.swift # SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupAPI+Poller.swift # SessionMessagingKit/Sending & Receiving/Pollers/Poller.swift # SessionMessagingKit/Utilities/ProfileManager.swift # SessionMessagingKitTests/Jobs/MessageSendJobSpec.swift # SessionMessagingKitTests/LibSession/LibSessionSpec.swift # SessionMessagingKitTests/LibSession/LibSessionUtilSpec.swift # SessionMessagingKitTests/Open Groups/Models/SOGSMessageSpec.swift # SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift # SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift # SessionMessagingKitTests/Utilities/CryptoSMKSpec.swift # SessionMessagingKitTests/_TestUtilities/MockOGMCache.swift # SessionNotificationServiceExtension/NSENotificationPresenter.swift # SessionNotificationServiceExtension/NotificationServiceExtension.swift # SessionShareExtension/ShareAppExtensionContext.swift # SessionShareExtension/ShareNavController.swift # SessionShareExtension/ThreadPickerVC.swift # SessionSnodeKit/Crypto/Crypto+SessionSnodeKit.swift # SessionSnodeKit/Models/DeleteAllBeforeResponse.swift # SessionSnodeKit/Models/DeleteAllMessagesResponse.swift # SessionSnodeKit/Models/DeleteMessagesResponse.swift # SessionSnodeKit/Models/RevokeSubkeyRequest.swift # SessionSnodeKit/Models/RevokeSubkeyResponse.swift # SessionSnodeKit/Models/SendMessageResponse.swift # SessionSnodeKit/Models/SnodeAuthenticatedRequestBody.swift # SessionSnodeKit/Models/UpdateExpiryAllResponse.swift # SessionSnodeKit/Models/UpdateExpiryResponse.swift # SessionSnodeKit/Networking/SnodeAPI.swift # SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift # SessionTests/Conversations/Settings/ThreadSettingsViewModelSpec.swift # SessionTests/Database/DatabaseSpec.swift # SessionTests/Settings/NotificationContentViewModelSpec.swift # SessionUIKit/Components/ToastController.swift # SessionUIKit/Style Guide/Values.swift # SessionUtilitiesKit/Crypto/Crypto+SessionUtilitiesKit.swift # SessionUtilitiesKit/Crypto/Crypto.swift # SessionUtilitiesKit/Database/Models/Identity.swift # SessionUtilitiesKit/Database/Models/Job.swift # SessionUtilitiesKit/Database/Storage.swift # SessionUtilitiesKit/Database/Types/Migration.swift # SessionUtilitiesKit/General/AppContext.swift # SessionUtilitiesKit/General/Data+Utilities.swift # SessionUtilitiesKit/General/Logging.swift # SessionUtilitiesKit/General/SNUserDefaults.swift # SessionUtilitiesKit/General/String+Trimming.swift # SessionUtilitiesKit/General/String+Utilities.swift # SessionUtilitiesKit/General/TimestampUtils.swift # SessionUtilitiesKit/General/UIEdgeInsets.swift # SessionUtilitiesKit/JobRunner/JobRunner.swift # SessionUtilitiesKit/LibSession/LibSessionError.swift # SessionUtilitiesKit/Media/DataSource.swift # SessionUtilitiesKit/Meta/SessionUtilitiesKit.h # SessionUtilitiesKit/Networking/NetworkType.swift # SessionUtilitiesKit/Networking/ProxiedContentDownloader.swift # SessionUtilitiesKit/Utilities/BackgroundTaskManager.swift # SessionUtilitiesKit/Utilities/BencodeResponse.swift # SessionUtilitiesKit/Utilities/CExceptionHelper.mm # SessionUtilitiesKit/Utilities/FileManagerType.swift # SessionUtilitiesKit/Utilities/KeychainStorageType.swift # SessionUtilitiesKitTests/Database/Models/IdentitySpec.swift # SessionUtilitiesKitTests/Database/Utilities/PersistableRecordUtilitiesSpec.swift # SessionUtilitiesKitTests/General/SessionIdSpec.swift # SessionUtilitiesKitTests/JobRunner/JobRunnerSpec.swift # SignalUtilitiesKit/Configuration.swift # SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentApprovalInputAccessoryView.swift # SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentApprovalViewController.swift # SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentTextToolbar.swift # SignalUtilitiesKit/Media Viewing & Editing/Image Editing/ImageEditorCropViewController.swift # SignalUtilitiesKit/Media Viewing & Editing/Image Editing/ImageEditorModel.swift # SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift # SignalUtilitiesKit/Meta/SignalUtilitiesKit.h # SignalUtilitiesKit/Shared View Controllers/OWSViewController.swift # SignalUtilitiesKit/Shared Views/CircleView.swift # SignalUtilitiesKit/Shared Views/TappableView.swift # SignalUtilitiesKit/Utilities/AppSetup.swift # SignalUtilitiesKit/Utilities/Bench.swift # SignalUtilitiesKit/Utilities/NoopNotificationsManager.swift # _SharedTestUtilities/CommonMockedExtensions.swift # _SharedTestUtilities/MockCrypto.swift # _SharedTestUtilities/Mocked.swift # _SharedTestUtilities/SynchronousStorage.swift
423 lines
15 KiB
Swift
423 lines
15 KiB
Swift
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
|
//
|
|
// stringlint:disable
|
|
|
|
import Foundation
|
|
|
|
// MARK: - BencodeEncoder
|
|
|
|
public struct BencodeEncoder {
|
|
private let dependencies: Dependencies
|
|
public var userInfo: [CodingUserInfoKey: Any]
|
|
|
|
public init(userInfo: [CodingUserInfoKey: Any] = [:], using dependencies: Dependencies) {
|
|
self.dependencies = dependencies
|
|
self.userInfo = userInfo
|
|
self.userInfo[Dependencies.userInfoKey] = dependencies
|
|
}
|
|
|
|
public func encode<T: Encodable>(_ value: T) throws -> Data {
|
|
let encoder: _BencodeEncoder = _BencodeEncoder(userInfo: userInfo, using: dependencies)
|
|
try value.encode(to: encoder)
|
|
|
|
switch encoder.container {
|
|
case let bencodeContainer as _BencodeEncoder.UnkeyedContainer:
|
|
return (encoder.data + (bencodeContainer.additionalData ?? Data()))
|
|
|
|
default: return encoder.data
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - AdditionalData Support
|
|
|
|
public extension UnkeyedEncodingContainer {
|
|
mutating func encodeAdditionalData<T>(_ data: T) throws where T: Encodable, T: DataProtocol {
|
|
let finalData: Data = try {
|
|
switch data {
|
|
case let value as Data: return value
|
|
case let value as [UInt8]: return Data(value)
|
|
default:
|
|
throw EncodingError.invalidValue(
|
|
data,
|
|
EncodingError.Context(
|
|
codingPath: codingPath,
|
|
debugDescription: "unable to encode additional data"
|
|
)
|
|
)
|
|
}
|
|
}()
|
|
|
|
switch self {
|
|
case let bencodeContainer as _BencodeEncoder.UnkeyedContainer: bencodeContainer.additionalData = finalData
|
|
default: try self.encode(finalData)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - _BencodeEncodingContainer
|
|
|
|
protocol _BencodeEncodingContainer: AnyObject {
|
|
var dependencies: Dependencies { get }
|
|
var data: Data { get }
|
|
}
|
|
|
|
// MARK: - _BencodeEncoder
|
|
|
|
final class _BencodeEncoder {
|
|
let dependencies: Dependencies
|
|
var codingPath: [CodingKey] = []
|
|
var userInfo: [CodingUserInfoKey: Any] = [:]
|
|
|
|
fileprivate var container: _BencodeEncodingContainer?
|
|
|
|
var data: Data {
|
|
return container?.data ?? Data()
|
|
}
|
|
|
|
init(codingPath: [CodingKey] = [], userInfo: [CodingUserInfoKey: Any], using dependencies: Dependencies) {
|
|
self.dependencies = dependencies
|
|
self.codingPath = codingPath
|
|
self.userInfo = userInfo
|
|
}
|
|
}
|
|
|
|
extension _BencodeEncoder: Encoder {
|
|
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
|
|
let container: KeyedContainer<Key> = KeyedContainer(codingPath: codingPath, userInfo: userInfo, using: dependencies)
|
|
self.container = container
|
|
|
|
return KeyedEncodingContainer(container)
|
|
}
|
|
|
|
func unkeyedContainer() -> UnkeyedEncodingContainer {
|
|
let container = UnkeyedContainer(codingPath: self.codingPath, userInfo: self.userInfo, using: dependencies)
|
|
self.container = container
|
|
|
|
return container
|
|
}
|
|
|
|
func singleValueContainer() -> SingleValueEncodingContainer {
|
|
let container = SingleValueContainer(codingPath: self.codingPath, userInfo: self.userInfo, using: dependencies)
|
|
self.container = container
|
|
|
|
return container
|
|
}
|
|
}
|
|
|
|
// MARK: - KeyedContainer
|
|
|
|
private extension _BencodeEncoder {
|
|
class KeyedContainer<Key> where Key: CodingKey {
|
|
private var storage: [String: (any _BencodeEncodingContainer)] = [:]
|
|
let dependencies: Dependencies
|
|
var codingPath: [CodingKey]
|
|
var userInfo: [CodingUserInfoKey: Any]
|
|
|
|
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any], using dependencies: Dependencies) {
|
|
self.dependencies = dependencies
|
|
self.codingPath = codingPath
|
|
self.userInfo = userInfo
|
|
}
|
|
}
|
|
}
|
|
|
|
extension _BencodeEncoder.KeyedContainer: _BencodeEncodingContainer {
|
|
var data: Data {
|
|
(
|
|
Data(Bencode.Element.dictIndicator.rawValue.utf8.map { UInt8($0) }) +
|
|
storage
|
|
.sorted(by: { $0.key < $1.key }) // Should be in lexicographical order
|
|
.map { key, value in
|
|
(
|
|
try! _BencodeEncoder.SingleValueContainer.encodedString(key, codingPath: codingPath) +
|
|
value.data
|
|
)
|
|
}
|
|
.reduce(Data(), +) +
|
|
Data(Bencode.Element.endIndicator.rawValue.utf8.map { UInt8($0) })
|
|
)
|
|
}
|
|
}
|
|
|
|
extension _BencodeEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
|
|
func encodeNil(forKey key: Key) throws {} // Just omit nil elements
|
|
|
|
func encode<T>(_ value: T, forKey key: Key) throws where T: Encodable {
|
|
var container = self.nestedSingleValueContainer(forKey: key)
|
|
try container.encode(value)
|
|
}
|
|
|
|
private func nestedSingleValueContainer(forKey key: Key) -> SingleValueEncodingContainer {
|
|
let container = _BencodeEncoder.SingleValueContainer(
|
|
codingPath: codingPath + [key],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
storage[key.stringValue] = container
|
|
return container
|
|
}
|
|
|
|
func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
|
|
let container = _BencodeEncoder.UnkeyedContainer(
|
|
codingPath: codingPath + [key],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
storage[key.stringValue] = container
|
|
return container
|
|
}
|
|
|
|
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
|
|
let container = _BencodeEncoder.KeyedContainer<NestedKey>(
|
|
codingPath: codingPath + [key],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
storage[key.stringValue] = container
|
|
return KeyedEncodingContainer(container)
|
|
}
|
|
|
|
func superEncoder() -> Encoder {
|
|
_BencodeEncoder(
|
|
codingPath: codingPath + [Bencode.SuperCodingKey()],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
}
|
|
func superEncoder(forKey key: Key) -> Encoder {
|
|
_BencodeEncoder(
|
|
codingPath: codingPath + [key],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
}
|
|
}
|
|
|
|
// MARK: - UnkeyedContainer
|
|
|
|
extension _BencodeEncoder {
|
|
final class UnkeyedContainer {
|
|
private var storage: [any _BencodeEncodingContainer] = []
|
|
let dependencies: Dependencies
|
|
var codingPath: [CodingKey]
|
|
var userInfo: [CodingUserInfoKey: Any]
|
|
var additionalData: Data?
|
|
|
|
var count: Int { return storage.count }
|
|
|
|
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any], using dependencies: Dependencies) {
|
|
self.dependencies = dependencies
|
|
self.codingPath = codingPath
|
|
self.userInfo = userInfo
|
|
}
|
|
}
|
|
}
|
|
|
|
extension _BencodeEncoder.UnkeyedContainer: _BencodeEncodingContainer {
|
|
var data: Data {
|
|
return (
|
|
Data(Bencode.Element.listIndicator.rawValue.utf8.map { UInt8($0) }) +
|
|
storage.map { $0.data }.reduce(Data(), +) +
|
|
Data(Bencode.Element.endIndicator.rawValue.utf8.map { UInt8($0) })
|
|
)
|
|
}
|
|
}
|
|
|
|
extension _BencodeEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
|
|
func encodeNil() throws {} // Just omit nil elements
|
|
|
|
func encode<T>(_ value: T) throws where T: Encodable {
|
|
var container = nestedSingleValueContainer()
|
|
try container.encode(value)
|
|
}
|
|
|
|
private func nestedSingleValueContainer() -> SingleValueEncodingContainer {
|
|
let container = _BencodeEncoder.SingleValueContainer(
|
|
codingPath: codingPath + [Bencode.UnkeyedCodingKey(intValue: storage.count)],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
storage.append(container)
|
|
return container
|
|
}
|
|
|
|
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
|
|
let container = _BencodeEncoder.KeyedContainer<NestedKey>(
|
|
codingPath: codingPath + [Bencode.UnkeyedCodingKey(intValue: storage.count)],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
storage.append(container)
|
|
return KeyedEncodingContainer(container)
|
|
}
|
|
|
|
func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
|
|
let container = _BencodeEncoder.UnkeyedContainer(
|
|
codingPath: codingPath + [Bencode.UnkeyedCodingKey(intValue: storage.count)],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
storage.append(container)
|
|
return container
|
|
}
|
|
|
|
func superEncoder() -> Encoder {
|
|
_BencodeEncoder(
|
|
codingPath: codingPath + [Bencode.UnkeyedCodingKey(intValue: storage.count)],
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
}
|
|
}
|
|
|
|
// MARK: - SingleValueContainer
|
|
|
|
extension _BencodeEncoder {
|
|
final class SingleValueContainer {
|
|
fileprivate var storage: Data = Data()
|
|
let dependencies: Dependencies
|
|
var codingPath: [CodingKey]
|
|
var userInfo: [CodingUserInfoKey: Any]
|
|
|
|
init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any], using dependencies: Dependencies) {
|
|
self.dependencies = dependencies
|
|
self.codingPath = codingPath
|
|
self.userInfo = userInfo
|
|
}
|
|
}
|
|
}
|
|
|
|
extension _BencodeEncoder.SingleValueContainer: _BencodeEncodingContainer {
|
|
var data: Data { storage }
|
|
}
|
|
|
|
extension _BencodeEncoder.SingleValueContainer: SingleValueEncodingContainer {
|
|
func encodeNil() throws {
|
|
throw EncodingError.invalidValue(0, EncodingError.Context(
|
|
codingPath: codingPath,
|
|
debugDescription: "Null values are not supported"
|
|
))
|
|
}
|
|
|
|
func encode(_ value: Int) throws {
|
|
let intValue: String = "\(Bencode.Element.intIndicator.rawValue)\(value)\(Bencode.Element.endIndicator.rawValue)"
|
|
storage = Data(intValue.utf8.map { UInt8($0) })
|
|
}
|
|
|
|
func encode(_ value: String) throws {
|
|
storage = try _BencodeEncoder.SingleValueContainer.encodedString(value, codingPath: codingPath)
|
|
}
|
|
|
|
func encode<T>(_ value: T) throws where T: Encodable {
|
|
// Encode non-primative encodable types as dictionaries
|
|
switch value {
|
|
case let intValue as Int: try encode(intValue)
|
|
case let stringValue as String: try encode(stringValue)
|
|
case let dataValue as Data: storage = encodedData(dataValue)
|
|
case let rawDataValue as Array<UInt8>: storage = encodedRawArray(rawDataValue)
|
|
case let arrayValue as any _ArrayProtocol: storage = try encodedArray(arrayValue)
|
|
case let dictValue as any _DictionaryProtocol: storage = try encodedDict(dictValue)
|
|
default:
|
|
let encoder = _BencodeEncoder(codingPath: codingPath, userInfo: userInfo, using: dependencies)
|
|
try value.encode(to: encoder)
|
|
storage = encoder.data
|
|
}
|
|
}
|
|
|
|
// MARK: - Explicit type encoding
|
|
|
|
fileprivate static func encodedString(_ value: String, codingPath: [CodingKey]) throws -> Data {
|
|
let encodedString: Data = try value.data(using: .ascii) ?? {
|
|
throw EncodingError.invalidValue(value, EncodingError.Context(
|
|
codingPath: codingPath,
|
|
debugDescription: "Could not convert string to ASCII data"
|
|
))
|
|
}()
|
|
let prefix: String = "\(encodedString.count)\(Bencode.Element.separator.rawValue)"
|
|
|
|
return (Data(prefix.utf8.map { UInt8($0) }) + encodedString)
|
|
}
|
|
|
|
private func encodedData(_ value: Data) -> Data {
|
|
// Data should be in the same format as a String
|
|
let prefix: String = "\(value.count)\(Bencode.Element.separator.rawValue)"
|
|
|
|
return (Data(prefix.utf8.map { UInt8($0) }) + value)
|
|
}
|
|
|
|
private func encodedRawArray(_ value: [UInt8]) -> Data {
|
|
// Bytes arrays should be in the same format as a String
|
|
let prefix: String = "\(value.count)\(Bencode.Element.separator.rawValue)"
|
|
|
|
return (Data(prefix.utf8.map { UInt8($0) }) + value)
|
|
}
|
|
|
|
private func encodedArray<T>(_ value: T) throws -> Data where T: _ArrayProtocol, T.Element: Encodable {
|
|
let result: [Data] = try value
|
|
.compactMap { element in
|
|
switch element {
|
|
case let dataValue as Data: return encodedData(dataValue)
|
|
case let rawDataValue as Array<UInt8>: return encodedRawArray(rawDataValue)
|
|
default:
|
|
let elementEncoder: _BencodeEncoder = _BencodeEncoder(
|
|
codingPath: codingPath,
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
try element.encode(to: elementEncoder)
|
|
return elementEncoder.data
|
|
}
|
|
}
|
|
|
|
return (
|
|
Data(Bencode.Element.listIndicator.rawValue.utf8.map { UInt8($0) }) +
|
|
Data(result.joined()) +
|
|
Data(Bencode.Element.endIndicator.rawValue.utf8.map { UInt8($0) })
|
|
)
|
|
}
|
|
|
|
private func encodedDict<T>(_ value: T) throws -> Data where T: _DictionaryProtocol, T.Key == String, T.Value: Encodable {
|
|
let encodedData: [Data] = try value
|
|
.sorted(by: { $0.key < $1.key }) // Should be in lexicographical order
|
|
.map { key, item in
|
|
let itemEncoder = _BencodeEncoder(
|
|
codingPath: codingPath,
|
|
userInfo: userInfo,
|
|
using: dependencies
|
|
)
|
|
try item.encode(to: itemEncoder)
|
|
|
|
return (
|
|
try _BencodeEncoder.SingleValueContainer.encodedString(key, codingPath: codingPath) +
|
|
itemEncoder.data
|
|
)
|
|
}
|
|
|
|
return (
|
|
Data(Bencode.Element.dictIndicator.rawValue.utf8.map { UInt8($0) }) +
|
|
Data(encodedData.joined()) +
|
|
Data(Bencode.Element.endIndicator.rawValue.utf8.map { UInt8($0) })
|
|
)
|
|
}
|
|
}
|
|
|
|
// MARK: - Convenience Protocols
|
|
|
|
private protocol _ArrayProtocol {
|
|
associatedtype Element: Encodable
|
|
|
|
func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
|
|
}
|
|
|
|
private protocol _DictionaryProtocol {
|
|
typealias Key = String
|
|
associatedtype Value: Encodable
|
|
|
|
func map<T>(_ transform: ((key: Key, value: Value)) throws -> T) rethrows -> [T]
|
|
func sorted(by areInIncreasingOrder: ((key: Key, value: Value), (key: Key, value: Value)) throws -> Bool) rethrows -> [(key: Key, value: Value)]
|
|
}
|
|
|
|
extension Array: _ArrayProtocol where Element: Encodable {}
|
|
extension Dictionary: _DictionaryProtocol where Key == String, Value: Encodable {}
|