如何在 Swift 中读取和写入内存块中的位

Lan*_*ard 3 memory bit-manipulation swift

我想知道如何将二进制文件读入内存(将其写入内存,就像 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]
  }
}
Run Code Online (Sandbox Code Playgroud)

我看了很多地方,但没有找到标准参考。

我希望这个级别尽可能低。所以也许使用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
Run Code Online (Sandbox Code Playgroud)

我基本上想分配一块内存并能够从中读取和写入。不知道该怎么做。也许使用 C是最好的方法,但不确定。

刚刚也看到了这个:

let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: width * height * 4)
Run Code Online (Sandbox Code Playgroud)

Joa*_*isO 6

如果您正在寻找低级代码,则需要使用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
Run Code Online (Sandbox Code Playgroud)

首先您需要打开该文件。这可以使用 Swift 字符串来完成,因为编译器将它们本身制作成 CString。

因为清理工作是在这个低级别上完成的,所以会延迟以在最后关闭文件。

接下来,将文件设置为寻找文件末尾。然后计算文件开头和结尾之间的距离。稍后会使用该值,因此保留该值。

然后程序设置为返回到文件的开头,因此应用程序从开头开始读取。

为了存储文件,将分配一个指针,该指针包含文件在文件系统中的字节数。注意:如果您非常不走运或者文件被频繁访问,则这可能会在步骤之间发生变化。但我认为对你来说,这不太可能。

设置字节数,并对齐到一个字节。(您可以在维基百科上了解有关内存对齐的更多信息。

然后添加另一个延迟以确保此代码末尾没有内存泄漏。需要手动释放指针。

文件的字节被读取并存储在指针中。请注意,整个过程以阻塞方式读取文件。异步读取文件可能是更好的选择,如果您打算这样做,我建议您查看像SwiftNIO这样的库。

errorOccurred可用于引发错误或以其他方式处理问题。

从这里开始,您的缓冲区就可以进行操作了。如果文件是文本,您可以使用以下代码打印该文件:

print(String(cString: pointer.bindMemory(to: Int8.self, capacity: fileByteSize)))
Run Code Online (Sandbox Code Playgroud)

从这里开始,是时候学习如何阅读和操纵记忆了。

操纵记忆

下面演示了将字节读取20..<24为 Int32。

let int32 = pointer.load(fromByteOffset: 20, as: Int32.self)
Run Code Online (Sandbox Code Playgroud)

我将把其他整数留给你。接下来,您还可以将数据放置在内存中的某个位置。

pointer.storeBytes(of: 40, toByteOffset: 30, as: Int64.self)
Run Code Online (Sandbox Code Playgroud)

这会将字节替换30..<38为数字 40。请注意,大端系统虽然不常见,但将以与普通小端系统不同的顺序存储信息。更多相关信息请参见此处。

修改位

正如您所指出的,您还对一次修改五位或十位感兴趣。为此,您需要将以前的信息与新信息混合起来。

var data32bits = pointer.load(fromByteOffset: 20, as: Int32.self)
var newData = 0b11111000
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您将对前 5 位感兴趣,并希望将它们写入第 2 位到第 7 位。为此,您首先需要将这些位移动到与新位置匹配的位置。

newData = newData >> 2
Run Code Online (Sandbox Code Playgroud)

这会将位向右移动 2 位。因此,现在剩下的两个空位是0。右边被推掉的 2 位不再存在。接下来,您需要从缓冲区获取旧数据并覆盖新位。为此,首先将新字节移至 32 位缓冲区中。

var newBits = numericCast(newData) as Int32
Run Code Online (Sandbox Code Playgroud)

32 位将一直向右对齐。如果要替换四个字节中的第二个,请运行以下命令:

newBits = newBits << 16
Run Code Online (Sandbox Code Playgroud)

这会将第四对左移 16 位,即 2 个字节。所以它现在位于从 0 开始的位置 1。

然后,需要将两个字节相互添加。一种常见的方法如下:

let oldBits = data32bits & 0b11111111_11000001_11111111_11111111
let result = oldBits | newBits
Run Code Online (Sandbox Code Playgroud)

这里发生的事情是,我们从旧数据集中删除了包含新数据的 5 位。我们通过对旧的 32 位和位图执行按位与来实现此目的。

除了要替换的新位置之外,该位图全部为 1。由于位图中这些位为空,因此 and 运算符将排除这些位,因为两者(旧数据与位图)之一为空。

仅当运算符两边都为 1 时,AND运算符才为 1。

最后,使用 OR 运算符将 oldBits 和 newBits 合并。如果两个位置的位均为 1,这将获取两侧的每一位并将结果设置为 1。这将成功合并,因为两个缓冲区都包含另一个数字未设置的 1 位。