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)
如果您正在寻找低级代码,则需要使用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 位。