Если вы ищете код низкого уровня, вам нужно будет использовать 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 бит, который не установлено другим числом.