Есть ли способ «преобразовать» данные в [UInt8] (байтовый массив) без копирования, или это небезопасный путь к go? - PullRequest
0 голосов
/ 21 февраля 2020

Я знаю, что могу сделать

//Setup
let originalBytes: [UInt8] = [0x0, 0x1, 0x2]
let originalData = Data(originalBytes)

//This
let getByteArFromData: [UInt8] = [UInt8](originalData)

Теперь я симпатичен уверен, что getByteArFromData КОПИРУЕТ байты в новую выделенную память (я здесь не прав?).

Это получает дорого по мере масштабирования данных (например, изображения, код c манипулирование и т. Д. c.).

Я мотивирован задать этот вопрос, потому что я хотел бы манипулировать данными через массивоподобный интерфейс, но, насколько мне известно, Unsafe API - это путь к go. Итак ...

Является ли использование Unsafe API единственным способом сделать это? Это рекомендуемый способ сделать это?

Я также играл с некоторой арифметикой указателей c (просто попадаю в небезопасный API, не уверен, стоит ли мне тратить время, если Я могу использовать интерфейс, похожий на массив).

Вот некоторый код игровой площадки (пожалуйста, оцените его и ответьте на вопросы в комментариях, если вы будете так любезны):

import Foundation

//MARK:- Setup

final class PlaygroundSwingSet
{
    //Squish unimportant setup code
    private let byteAr: [UInt8]
    private lazy var data: Data = {Data(byteAr)}()
    init(_ bytes: [UInt8]) {self.byteAr = bytes}

    func swing() -> Int
    {
        //Unsafe API stuff
        return data.withUnsafeBytes {(dataBufferPtr) -> (Int) in   //No need for [weak self], right?
            //I don't like how the pointers are only valid in this closure.
            //It adds an indent to my code to which my "code OCD" forces me to abstract out
            self.middleOut(dataBufferPtr)
        }
    }

    private func middleOut(_ dataBufferPtr: UnsafeRawBufferPointer) -> Int
    {
        //Yuck, I have to make sure count isn't 0 AND safely unwrap.
        //Why is .baseAddress even optional then?
        guard let dataPtr = dataBufferPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), dataBufferPtr.count > 0 else {
            print("data is empty")
            return 0
        }
        let middishIndex = dataBufferPtr.count / 2 - 1

        //More yuck.
        print("truncated middle element's value is \(dataPtr.advanced(by: middishIndex).pointee)")
        print("this should yield the same thing: \((dataPtr + middishIndex).pointee)")
        return middishIndex
    }
}

//MARK:- Code Execution

let a = PlaygroundSwingSet([0x1, 0x2, 0x3, 0x4]).swing() //a = 1
//Console prints
/*
 truncated middle element's value is 3
 this should yield the same thing: 2
 */
let b = PlaygroundSwingSet([]).swing()  //b = 0
//Console prints
/*
 data is empty
 */

1 Ответ

2 голосов
/ 21 февраля 2020

Нет необходимости приводить данные, поскольку он поддерживает оператор индекс для доступа к UInt8 по заданному индексу.

Кстати, он также поддерживает isEmpty, так что вы делаете не нужно проверять, равно ли число 0.

var data = Data(repeating: 0, count: 5)
data.indices.forEach { data[$0] = UInt8($0) }
print(data[2])
...