Как лучше всего записать структуру в файл?
У меня есть две структуры:
struct pcap_hdr_s {
UInt32 magic_number;
UInt16 version_major;
UInt16 version_minor;
int32_t thiszone;
UInt32 sigfigs;
UInt32 snaplen;
UInt32 network;
};
//packet header
struct pcaprec_hdr_s {
UInt32 ts_sec;
UInt32 ts_usec;
UInt32 incl_len;
UInt32 orig_len;
};
Которые инициализируются следующим образом (например):
let pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number: 0xa1b2c3d4,
version_major: 2,
version_minor: 4,
thiszone: 0,
sigfigs: 0,
snaplen:
pcap_record_size,
network: LINKTYPE_ETHERNET)
let pcapRecHeader : pcaprec_hdr_s = pcaprec_hdr_s(ts_sec: UInt32(ts.tv_sec),
ts_usec: UInt32(ts.tv_nsec),
incl_len: plen,
orig_len: length)
Я попытался создать объекты Data / NSData структур следующим образом:
//write pcap header
let pcapHeaderData : NSData = NSData(bytes: pcapHeader, length: sizeofValue(pcapHeader))
//write pcaprec header
let pcapRecHeaderData : NSData = NSData(bytes: pcapRecHeader, length: sizeofValue(pcapRecHeader))
Но я всегда получаю эту ошибку для каждой строки:
"Connot convert value if type 'pcap_hdr_s' to expected arguemnt type 'UsafeRawPointer?'"
Я посмотрел документацию UnsafeRawPointers в Swift, но я не получаю ее достаточно, чтобы создать объект NSData из структур.
Я на правильном пути или есть лучший способ выполнить мое намерение?
Если эта инициализация данных будет работать, мои следующие шаги будут
- Добавить pcapRecHeaderData к pcapHeaderData
- запишите pcapHeaderData атомарно в файл / url с предоставленной функцией Data / NSData
Правка:
//packet ethernet header
struct ethernet_hdr_s {
let dhost : [UInt8]
let shost : [UInt8]
let type : UInt16
};
let src_mac : [UInt8] = [0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB]
let dest_mac : [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]
let ethernetHeader : ethernet_hdr_s = ethernet_hdr_s(dhost: dest_mac, shost: src_mac, type: 0x0800)
Правка 2:
let payloadSize = packet.payload.count
let plen = (payloadSize < Int(pcap_record_size) ? payloadSize : Int(pcap_record_size));
bytesWritten = withUnsafePointer(to: &(packet.payload)) {
$0.withMemoryRebound(to: UInt8.self, capacity: Int(plen)) {
ostream.write($0, maxLength: Int(plen))
}
}
if bytesWritten != (Int(plen)) {
// Could not write all bytes, report error ...
NSLog("error in Writting packet payload, not all Bytes written: bytesWritten: %d|plen: %d", bytesWritten, Int(plen))
}
2 ответов:
Вы можете записать произвольные данные в
InputStream, не создавая(NS)Dataобъект первый. "Проблема" заключается в том, как преобразовать указатель в структура к указателюUInt8, как и ожидалось методомwrite:let ostream = OutputStream(url: url, append: false)! // Add error checking here! ostream.open() var pcapHeader = pcap_hdr_s(...) let headerSize = MemoryLayout.size(ofValue: pcapHeader) let bytesWritten = withUnsafePointer(to: &pcapHeader) { $0.withMemoryRebound(to: UInt8.self, capacity: headerSize) { ostream.write($0, maxLength: headerSize) } } if bytesWritten != headerSize { // Could not write all bytes, report error ... }Таким же образом можно читать данные из in
InputStream:let istream = InputStream(url: url)! // Add error checking here! istream.open() let bytesRead = withUnsafeMutablePointer(to: &pcapHeader) { $0.withMemoryRebound(to: UInt8.self, capacity: headerSize) { istream.read($0, maxLength: headerSize) } } if bytesRead != headerSize { // Could not read all bytes, report error ... }Если файл, возможно, был создан на другой платформе с другой порядок байтов, то вы можете проверить "волшебство" и поменять местами байты при необходимости (как описано на https://wiki.wireshark.org/Development/LibpcapFileFormat):
switch pcapHeader.magic_number { case 0xa1b2c3d4: break // Already in host byte order case 0xd4c3b2a1: pcapHeader.version_major = pcapHeader.version_major.byteSwapped pcapHeader.version_minor = pcapHeader.version_minor.byteSwapped // ... default: // Unknown magic, report error ... }
Для упрощения задачи записи и чтения структур можно определить: пользовательские методы расширения, напримерextension OutputStream { enum ValueWriteError: Error { case incompleteWrite case unknownError } func write<T>(value: T) throws { var value = value let size = MemoryLayout.size(ofValue: value) let bytesWritten = withUnsafePointer(to: &value) { $0.withMemoryRebound(to: UInt8.self, capacity: size) { write($0, maxLength: size) } } if bytesWritten == -1 { throw streamError ?? ValueWriteError.unknownError } else if bytesWritten != size { throw ValueWriteError.incompleteWrite } } } extension InputStream { enum ValueReadError: Error { case incompleteRead case unknownError } func read<T>(value: inout T) throws { let size = MemoryLayout.size(ofValue: value) let bytesRead = withUnsafeMutablePointer(to: &value) { $0.withMemoryRebound(to: UInt8.self, capacity: size) { read($0, maxLength: size) } } if bytesRead == -1 { throw streamError ?? ValueReadError.unknownError } else if bytesRead != size { throw ValueReadError.incompleteRead } } }Теперь вы можете писать и читать просто с помощью
try ostream.write(value: pcapHeader) try istream.read(value: &pcapHeader)Конечно, это работает только с "автономными" структурами, такими как ваш
pcap_hdr_sиpcaprec_hdr_s.
Вы можете преобразовать
pcap_hdr_sвDataи наоборот в Swift 3 с помощью
pcap_hdr_s->Datavar pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number ... let data = withUnsafePointer(to: &pcapHeader) { Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: pcapHeader)) }
Data->pcap_hdr_slet header: pcap_hdr_s = data.withUnsafeBytes { $0.pointee }
Comments