Как читать и писать биты в куске памяти в Swift - PullRequest
1 голос
/ 15 марта 2019

Я хотел бы знать, как считывать двоичный файл в память (записывать его в память как «буфер массива» из JavaScript) и записывать в разные части памяти 8-битный, 16-битный, 32-битный и т. Д..значения, даже 5-битные или 10-битные значения.

extension Binary {
  static func readFileToMemory(_ file) -> ArrayBuffer {        
    let data = NSData(contentsOfFile: "/path/to/file/7CHands.dat")!
    var dataRange = NSRange(location: 0, length: ?)
    var ? = [Int32](count: ?, repeatedValue: ?)
    data.getBytes(&?, range: dataRange)
  }

  static func writeToMemory(_ buffer, location, value) {
    buffer[location] = value
  }

  static func readFromMemory(_ buffer, location) {
    return buffer[location]
  }
}

Я просмотрел несколько мест, но не нашел стандартную ссылку.

Я бы хотел, чтобы был как можно более низким уровнем .Так что, возможно, используя UnsafeMutablePointer, UnsafePointer или UnsafeMutableRawPointer.

Пила также :

let data = NSMutableData()
var goesIn: Int32 = 42
data.appendBytes(&goesIn, length: sizeof(Int32))
println(data) // <2a000000]

var comesOut: Int32 = 0
data.getBytes(&comesOut, range: NSMakeRange(0, sizeof(Int32)))
println(comesOut) // 42

Я бы хотел выделить часть памяти и иметь возможность читать и писать из нее.Не уверен, как это сделать.Возможно, с использованием C - лучший способ, не уверен.

Только что тоже видел:

let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: width * height * 4)

1 Ответ

0 голосов
/ 19 марта 2019

Если вы ищете код низкого уровня, вам нужно будет использовать UnsafeMutableRawPointer. Это указатель на нетипизированные данные. Доступ к памяти осуществляется в байтах, поэтому 8 блоков по крайней мере 8 бит. Сначала я покрою кратные 8 битов.

Чтение файла

Чтобы прочитать файл таким образом, вам нужно самостоятельно управлять дескрипторами файлов и указателями. Попробуйте следующий код:

// Open the file in read mode
let file = fopen("/Users/joannisorlandos/Desktop/ownership", "r")

// Files need to be closed manually
defer { fclose(file) }

// Find the end
fseek(file, 0, SEEK_END)
// Count the bytes from the start to the end
let fileByteSize = ftell(file)
// Return to the start
fseek(file, 0, SEEK_SET)

// Buffer of 1 byte entities
let pointer = UnsafeMutableRawPointer.allocate(byteCount: fileByteSize, alignment: 1)

// Buffer needs to be cleaned up manually
defer { pointer.deallocate() }

// Size is 1 byte
let readBytes = fread(pointer, 1, fileByteSize, file)
let errorOccurred = readBytes != fileByteSize

Сначала вам нужно открыть файл. Это можно сделать с помощью строк Swift, поскольку компилятор превращает их в саму CString.

Поскольку очистка для нас на этом низком уровне, для закрытия файла в конце ставится отсрочка.

Далее файл настроен на поиск конца файла. Затем вычисляется расстояние между началом файла и концом. Это используется позже, поэтому значение сохраняется.

Затем программа возвращается к началу файла, поэтому приложение начинает чтение с самого начала.

Для хранения файла указатель выделяется с количеством байтов, которые файл имеет в файловой системе. Примечание. Это может измениться между шагами, если вам крайне не повезло или к файлу обращаются довольно часто. Но я думаю, что для вас это маловероятно.

Количество байтов установлено и выровнено по одному байту. (Вы можете узнать больше о выравнивании памяти в Wikipedia .

Затем добавляется еще одна задержка, чтобы убедиться в отсутствии утечек памяти в конце этого кода. Указатель необходимо освободить вручную.

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

errorOccurred может использоваться, чтобы вызвать ошибку или решить проблемы другим способом.

Отсюда ваш буфер готов к манипуляциям. Вы можете распечатать файл, если он текстовый, используя следующий код:

print(String(cString: pointer.bindMemory(to: Int8.self, capacity: fileByteSize)))

Отсюда пришло время научиться читать, манипулировать памятью.

Управление памятью

Ниже показано чтение байта 20..<24 как Int32.

let int32 = pointer.load(fromByteOffset: 20, as: Int32.self)

Я оставлю другие целые числа на ваше усмотрение. Затем вы можете поместить данные в определенную позицию в памяти.

pointer.storeBytes(of: 40, toByteOffset: 30, as: Int64.self)

Это заменит байт 30..<38 на число 40. Обратите внимание, что системы с прямым порядком байтов, хотя и редко, будут хранить информацию в другом порядке, чем обычные системы с прямым порядком байтов. Подробнее об этом здесь.

Модифицирующие биты

Как вы заметили, вы также заинтересованы в изменении пяти или десяти битов одновременно. Для этого вам нужно смешать предыдущую информацию с новой информацией.

var data32bits = pointer.load(fromByteOffset: 20, as: Int32.self)
var newData = 0b11111000

В этом случае вас заинтересуют первые 5 битов, и вы захотите записать их в биты со 2 по 7. Для этого сначала вам нужно переместить биты в позицию, соответствующую новой позиции.

newData = newData >> 2

Это сдвигает биты на 2 позиции вправо. Поэтому два левых бита, которые теперь пусты, равны 0. Две биты справа, которые были отброшены, больше не существуют. Затем вы захотите получить старые данные из буфера и перезаписать новые биты. Для этого сначала переместите новый байт в 32-битный буфер.

var newBits = numericCast(newData) as Int32

32 бита будут выровнены полностью вправо. Если вы хотите заменить второй из четырех байтов, выполните следующее:

newBits = newBits << 16

Перемещает четвертую пару на 16 битных мест влево или 2 байта. Так что теперь он на позиции 1, начиная с 0.

Затем два байта должны быть добавлены друг к другу. Один из распространенных методов заключается в следующем:

let oldBits = data32bits & 0b11111111_11000001_11111111_11111111
let result = oldBits | newBits

Что здесь происходит, так это то, что мы удаляем 5 бит с новыми данными из старого набора данных. Мы делаем это, делая поразрядно и по старым 32-битным и растровым изображениям.

Растровое изображение имеет все 1, кроме новых местоположений, которые заменяются.Поскольку они являются пустыми в растровом изображении, оператор и исключит эти биты, поскольку один из двух (старые данные и растровое изображение) пуст. *

И Операторы будут только 1, если обаСторонами оператора являются 1.

Наконец, старые и новые биты объединяются с оператором ИЛИ.Это займет каждый бит с обеих сторон и установит результат в 1, если биты в обеих позициях равны 1. Это будет успешно объединено, так как оба буфера содержат 1 бит, который не установлено другим числом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...