Быстрые мутации данных на расстоянии - PullRequest
0 голосов
/ 25 сентября 2018

Я создаю Data объект, который выглядит примерно так:

struct StructuredData {
  var crc: UInt16
  var someData: UInt32
  var someMoreData: UInt64
  // etc.
}

Я использую алгоритм CRC, который начинается с байта 2 и длины процесса 12.

Когда CRC возвращается, он должен существовать в начале объекта Data.На мой взгляд, у меня есть следующие варианты:

  1. Создать объект Data, который не включает в себя CRC, обработать его, а затем построить еще один объект Data, который выполняет (чтобызначение CRC, которое у меня сейчас есть, будет в начале объекта Data.

  2. Создайте объект данных, чтобы включить CRC с нулевым нулем для начала, а затем изменитеданные в диапазоне [0..<2].

Очевидно, что 2 будет предпочтительнее, так как использует меньше памяти и меньше обработки, но я не уверен, что этот тип оптимизации больше необходим.Я все же предпочел бы перейти с 2, за исключением того, что я не знаю, как изменить данные в заданном диапазоне индекса. Любая помощь очень ценится.

Ответы [ 2 ]

0 голосов
/ 25 сентября 2018

Я не рекомендую способ мутировать Data, используя это:

data.replaceSubrange(0..<2, with: UnsafeBufferPointer(start: &self.crc, count: 1))

Пожалуйста, попробуйте это:

data.replaceSubrange(0..<2, with: &self.crc, count: 2)

Трудно объяснить, почему, ноЯ попробую ...

В Swift параметры inout работают в семантике копирование-в-копирование.Когда вы пишете что-то вроде этого:

aMethod(&param)
  • Swift выделяет некоторую область достаточного размера для содержания param,

  • копирует param в регион, (copy-in)

  • вызывает метод с передачей адреса региона,

  • и когдаПри возврате из вызова копирует содержимое региона обратно в param (копирование).

Во многих случаях Swift оптимизирует (что может произойти даже в настройке -Onone) шаги, просто передавая фактический адрес param, но он явно не задокументирован.

Итак, когда параметр inout передается инициализатору UnsafeBufferPointer, адрес, полученный UnsafeBufferPointer может указывать на временную область, которая будет освобождена сразу после завершения инициализации.

Таким образом, replaceSubrange(_:with:) может скопировать байты в уже освобожденной области в Data.

Я полагаю, что первый код будет работать в этом случае, так как crc является свойством структурыt, но если есть простая и безопасная альтернатива, вам лучше избегать небезопасного пути.


ДОПОЛНИТЕЛЬНО для комментария к собственному ответу Брэндона Манцзы.

data.append(UnsafeBufferPointer(start: &self.crcOfRecordData, count: 1))

Использование сейф в значении выше.Это не безопасно , по той же причине, описанной выше.

Я бы написал это как:

data.append(Data(bytes: &self.crcOfRecordData, count: MemoryLayout<UInt16>.size))

(Предполагая тип crcOfRecordData как UInt16.)

Если вы не предпочитаете создавать дополнительный Data экземпляр, вы можете написать его как:

withUnsafeBytes(of: &self.crcOfRecordData) {urbp in
     data.append(urbp.baseAddress!.assumingMemoryBound(to: UInt8.self), count: MemoryLayout<UInt16>.size)
}

Это не упоминается в комментарии, но взначение выше safe , следующая строка не является safe .

let uint32Data = Data(buffer: UnsafeBufferPointer(start: &self.someData, count: 1))

Все по той же причине.

Я бы написал это как:

let uint32Data = Data(bytes: &self.someData, count: MemoryLayout<UInt32>.size)

Хотя наблюдаемое неожиданное поведение может происходить в очень ограниченном состоянии и с очень малой вероятностью.

Такое поведение может иметь место только при соблюдении следующих двух условий:

  1. Компилятор Swift генерирует неоптимизированный код "копировать-в-копировать"

  2. Между очень узким периодом, поскольку освобождается временная областьпока метод append (или Data.init) не закончит копирование всего содержимого, регион будет изменен для другого использования.

Условие № 1 становится истинным только в ограниченных случаях в текущей реализации Swift.

Условие № 2 возникает очень редко только в многопоточной среде.(Тем не менее, платформа Apple использует много скрытых потоков, как вы можете найти в отладчике XCode.)

На самом деле, я не видел никаких вопросов, касающихся небезопасных случаев выше, мой safe может быть чем-то вроде overkill .

Но альтернативные safe коды не так сложны, не так ли?На мой взгляд, вам лучше привыкнуть использовать код, безопасный для всех случаев .

0 голосов
/ 25 сентября 2018

Я понял это.У меня фактически была синтаксическая ошибка, которая меня поражала, потому что я раньше этого не видел.

Вот ответ:

data.replaceSubrange(0..<2, with: UnsafeBufferPointer(start: &self.crc, count: 1))
...