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

时间:2019-03-15 12:25:06

标签: swift memory bit-manipulation

我想知道如何将二进制文件读入内存(将其像JavaScript中的“ Array Buffer”一样写入内存),以及写入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]
  }
}

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

我希望这个尽可能低级。因此,也许使用UnsafeMutablePointerUnsafePointerUnsafeMutableRawPointer

也看到了this

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

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

也看到了这一点:

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

1 个答案:

答案 0 :(得分:0)

如果您要查找低级代码,则需要使用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)

这将用数字40替换字节30..<38。请注意,大字节序系统尽管不常见,但其存储信息的顺序与普通小字节序系统不同。 More about that here.

修改位

如您所述,您还希望一次修改5或10位。为此,您需要将先前的信息与新的信息混合在一起。

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

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

newData = newData >> 2

这会将位右移2位。因此,现在为空的左两位为0。右侧的2位不再存在。 接下来,您将要从缓冲区中获取旧数据并覆盖新位。 为此,首先将新字节移到32位缓冲区中。

var newBits = numericCast(newData) as Int32

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

newBits = newBits << 16

这会将第四对移动16位或2个字节。现在它从0开始位于位置1。

然后,这两个字节需要彼此叠加。一种常见的方法如下:

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

这里发生的是,我们从旧数据集中删除了带有新数据的5位。为此,我们对旧的32位和位图进行了按位和操作。

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

如果

AND 运算符的两边均为1,则它们将为1。

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