I have this snippet:
let mut animation_index = 0 as usize;
let mut ptr : *mut usize = &mut animation_index as _;
{
io_context.window().add_key_callback(
Box::new(move |key_states| {
if key_states[KbKey::Space.to_index()] == KeyActionState::Press
{
unsafe {
*ptr += 1;
println!("{}", animation_index);
}
}
})
);
}
Basically it adds a callback such that if and when I press space, the integer variable animation_index goes up by 1. This works, but requires the use of mutable pointers and unsafe, which is very ugly.
I'd like to have the same logic but ideally do it with pure safe rust isntead.
It looks like you are trying to share a mutable value across threads.
Typically, this is done with atomics, Arc<Mutex<T>> or Arc<RwLock<T>>.
use std::synce::{Arc, RwLock};
let mut animation_index = Arc::new(RwLock::new(0usize));
{
// a clone of the counter that can be moved into the callback
let animation_index = animation_index.clone();
io_context.window().add_key_callback(
Box::new(move |key_states| {
if key_states[KbKey::Space.to_index()] == KeyActionState::Press
{
let index = animation_index.write().unwrap();
*index += 1;
println!("{}", index);
}
})
);
}
With atomics it would look something like this:
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
let mut animation_index = Arc::new(AtomicUsize::new(0));
{
// a clone of the counter that can be moved into the callback
let animation_index = animation_index.clone();
io_context.window().add_key_callback(
Box::new(move |key_states| {
if key_states[KbKey::Space.to_index()] == KeyActionState::Press
{
let index = animation_index.fetch_add(1, Ordering::SeqCst);
println!("{}", index);
}
})
);
}
Currently, I'm getting an EXC_BAD_ACCESS error on the audio thread, and I'm trying to deduce what is going wrong.
When converting .wav file data from Data to an AVAudioPCMBuffer, do I need to strip the RIFF header first?
import AVFoundation
public class Player : NSObject {
let engine = AVAudioEngine()
public override init() {
super.init()
do {
let _ = engine.mainMixerNode
try engine.start()
} catch {
print("Player error: \(error)")
}
}
#objc public func play(_ data: Data) {
let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 48000, channels: 2, interleaved: true)!
let buffer = data.toPCMBuffer(format: format)!
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: nil)
player.scheduleBuffer(buffer, at: nil, completionCallbackType: .dataPlayedBack) {
callbackType in
// Nothing in here.
}
player.play()
}
}
Here's the toPCMBuffer extension:
// Taken from: https://stackoverflow.com/a/52731480/2228559
extension Data {
func toPCMBuffer(format: AVAudioFormat) -> AVAudioPCMBuffer? {
let streamDesc = format.streamDescription.pointee
let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }
buffer.frameLength = buffer.frameCapacity
let audioBuffer = buffer.audioBufferList.pointee.mBuffers
withUnsafeBytes { addr in
audioBuffer.mData?.copyMemory(from: addr, byteCount: Int(audioBuffer.mDataByteSize))
}
return buffer
}
}
Note: I cannot use AVAudioFile because the .wav file data is loaded over-the-wire.
IDK, but my mac crashes if I play interleaved AVAudioPCMBuffers, and garbled audio if they're not float data, so you could convert to non-interleaved float data:
#objc public func play(_ data: Data) {
let sampleRate: Double = 48000
let interleavedFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: sampleRate, channels: 2, interleaved: true)!
let interleavedBuffer = data.toPCMBuffer(format: interleavedFormat)!
let nonInterleavedFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: sampleRate, channels: 2, interleaved: false)!
let nonInterleavedBuffer = AVAudioPCMBuffer(pcmFormat: nonInterleavedFormat, frameCapacity: interleavedBuffer.frameCapacity)!
nonInterleavedBuffer.frameLength = interleavedBuffer.frameLength
let converter = AVAudioConverter(from: interleavedFormat, to: nonInterleavedFormat)!
try! converter.convert(to: nonInterleavedBuffer, from: interleavedBuffer)
let player = AVAudioPlayerNode()
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: nil)
player.scheduleBuffer(nonInterleavedBuffer, at: nil, completionCallbackType: .dataPlayedBack) {
callbackType in
// Nothing in here.
}
player.play()
}
extension Data {
func toPCMBuffer(format: AVAudioFormat) -> AVAudioPCMBuffer? {
assert(format.isInterleaved)
let streamDesc = format.streamDescription.pointee
let frameCapacity = UInt32(count) / streamDesc.mBytesPerFrame
guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCapacity) else { return nil }
buffer.frameLength = buffer.frameCapacity
let b = UnsafeMutableBufferPointer(start: buffer.int16ChannelData![0], count: buffer.stride * Int(frameCapacity))
let bytesCopied = self.copyBytes(to: b)
assert(bytesCopied == count)
return buffer
}
}
I have implemented function to detect USB device. It works and now i need to send/read data.
I started look over a lot of obj-c sources and found only one good article in apple documentation, that describes how do we can send package to our USB device:
IOReturn WriteToDevice(IOUSBDeviceInterface **dev, UInt16 deviceAddress,
UInt16 length, UInt8 writeBuffer[])
{
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor,
kUSBDevice);
request.bRequest = 0xa0;
request.wValue = deviceAddress;
request.wIndex = 0;
request.wLength = length;
request.pData = writeBuffer;
return (*dev)->DeviceRequest(dev, &request);
}
But I didn't find a way how to create and send data with Swift. The struct on Swift looks like:
public struct IOUSBDevRequest {
public var bmRequestType: UInt8
public var bRequest: UInt8
public var wValue: UInt16
public var wIndex: UInt16
public var wLength: UInt16
public var pData: UnsafeMutableRawPointer!
public var wLenDone: UInt32
public init()
public init(bmRequestType: UInt8, bRequest: UInt8, wValue: UInt16, wIndex: UInt16, wLength: UInt16, pData: UnsafeMutableRawPointer!, wLenDone: UInt32)
}
I can't figure out what parameters is pData, zwLenDone.
This is data that i need to send:
{
'direction':'in',
'recipient':'device',
'requestType': 'standard',
'request': 6,
'value': 0x300,
'index': 0,
'length': 255
}
The next question is: How i can receive data. I know the answer is in this article, but i can't convert it to Swift.
Here is what i could converted on Swift 3. My class detects USB device, get his configuration:
class DFUDevice: NSObject {
let vendorId = 0x0483
let productId = 0xdf11
static let sharedInstance = DFUDevice()
var deviceName:String = ""
private func deviceAdded(iterator: io_iterator_t) {
var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?
var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
var configPtr:IOUSBConfigurationDescriptorPtr?
var score: Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
// although in device_types.h it's defined:
// typedef char io_name_t[128];
var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128)
let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)
if(deviceNameResult != kIOReturnSuccess) {
print("Error getting device name")
}
self.deviceName = String.init(cString: &deviceNameCString)
print("usb Device Name: \(deviceName)")
// Get plugInInterface for current USB device
let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterfacePtrPtr,
&score)
// dereference pointer for the plug in interface
guard plugInInterfaceResult == kIOReturnSuccess,
let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Plug-In Interface")
continue
}
// use plug in interface to get a device interface
let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
$0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
plugInInterface.QueryInterface(
plugInInterfacePtrPtr,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
$0)
}
}
// dereference pointer for the device interface
guard deviceInterfaceResult == kIOReturnSuccess,
let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
continue
}
var ret = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr)
if (ret == kIOReturnSuccess)
{
// set first configuration as active
ret = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr)
if (ret != kIOReturnSuccess)
{
print("Could not set active configuration (error: %x)\n", ret);
continue
}
guard let config = configPtr?.pointee else {
continue
}
if config.bLength > 0 {
//HERE I NEED SEND DATA
} else {
print("ConfigurationDescriptor not valid")
}
print(config.bLength)
}
else if (ret == kIOReturnExclusiveAccess)
{
// this is not a problem as we can still do some things
}
else
{
print("Could not open device (error: %x)\n", ret)
continue
}
IOObjectRelease(usbDevice)
}
}
func initUsb() {
var matchedIterator:io_iterator_t = 0
var removalIterator:io_iterator_t = 0
let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector"))
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId)
matchingDict[kUSBProductID] = NSNumber(value: self.productId)
let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.deviceAdded(iterator: iterator)
this.connected(iterator: iterator)
}
let removalCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.disconnected(iterator: iterator)
}
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator)
IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator)
self.deviceAdded(iterator: matchedIterator)
self.deviceAdded(iterator: removalIterator)
RunLoop.current.run()
}
}
I call it like:
let DFUDeviceDaemon = Thread(target: DFUDevice.sharedInstance, selector:#selector(DFUDevice.initUsb), object: nil)
DFUDeviceDaemon.start()
The article you reference has a function called WriteToDevice. One of its parameters is
UInt8 writeBuffer[]
This writeBuffer, the data that you want to send, is a C array of bytes:
uint8_t msgLength = 3;
uint8_t writeBuffer[msgLength];
writeBuffer[0] = 0x41; // ASCII 'A'
writeBuffer[1] = 0x42; // ASCII 'B'
writeBuffer[2] = 0x43; // ASCII 'C'
What bytes do you need to send? That really depends on the device at the other end -- the technical data from the manufacturer should tell you that.
To pass the C-array as NSData, which is probably what pData is, you'd use:
NSData *data = [NSData dataWithBytes:&writeBuffer length:3];
The (z)wLenDone is probably what I called the msgLength, 3. C-array's have no knowledge of their own length, so most functions require the length as a separate parameter.
As for receiving data, I would guess that happens in the matchingCallback: you use the iterator to receive the bytes and then parse them.
ANSWER TO COMMENT:
I'm not familiar with C#, and I'm no expert at this stuff, but maybe this will help:
var package = new UsbSetupPacket(
(byte)(UsbCtrlFlags.Direction_In |
UsbCtrlFlags.Recipient_Device |
UsbCtrlFlags.RequestType_Standard), // Index
6, // length of data, second phase
0x200, // Request
0, // RequestType
(short)length); // Value
A few observations: know nothing of C#, but should not the package be typed to struct? RequestType is 0, so you will receive no response -- is that what you want? Or did you want to send UsbCtrlFlags.RequestType_Standard as the fourth parameter? Why send the length as a value?
Anyway, what you do now is send the package to the USB device and see what happens.
After a lot of questions on stackoverflow and learning sources i figure it out:
First define not implemented functions
import Foundation
import IOKit
import IOKit.usb
import IOKit.usb.IOUSBLib
//from IOUSBLib.h
let kIOUSBDeviceUserClientTypeID = CFUUIDGetConstantUUIDWithBytes(nil,
0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4,
0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
let kIOUSBDeviceInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4,
0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)
//from IOCFPlugin.h
let kIOCFPlugInInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil,
0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4,
0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F)
/*!
#defined USBmakebmRequestType
#discussion Macro to encode the bRequest field of a Device Request. It is used when constructing an IOUSBDevRequest.
*/
func USBmakebmRequestType(direction:Int, type:Int, recipient:Int) -> UInt8 {
return UInt8((direction & kUSBRqDirnMask) << kUSBRqDirnShift)|UInt8((type & kUSBRqTypeMask) << kUSBRqTypeShift)|UInt8(recipient & kUSBRqRecipientMask)
}
Then create our class:
extension Notification.Name {
static let dfuDeviceConnected = Notification.Name("DFUDeviceConnected")
static let dfuDeviceDisconnected = Notification.Name("DFUDeviceDisconnected")
}
class DFUDevice: NSObject {
let vendorId = 0x0483
let productId = 0xdf11
static let sharedInstance = DFUDevice()
var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?
var interfacePtrPtr:UnsafeMutablePointer<UnsafeMutablePointer<IOUSBInterfaceInterface>?>?
private func rawDeviceAdded(iterator: io_iterator_t) {
var score:Int32 = 0
var kr:Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
// although in device_types.h it's defined:
// typedef char io_name_t[128];
var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128)
let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)
if(deviceNameResult != kIOReturnSuccess) {
print("Error getting device name")
}
let deviceName = String.init(cString: &deviceNameCString)
print("usb Device Name: \(deviceName)")
// Get plugInInterface for current USB device
let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterfacePtrPtr,
&score)
// USB device object is no longer needed.
IOObjectRelease(usbDevice)
// Dereference pointer for the plug-in interface
guard plugInInterfaceResult == kIOReturnSuccess,
let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Plug-In Interface")
continue
}
// use plug in interface to get a device interface
let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
$0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
plugInInterface.QueryInterface(
plugInInterfacePtrPtr,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
$0)
}
}
// dereference pointer for the device interface
guard deviceInterfaceResult == kIOReturnSuccess,
let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
continue
}
kr = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr)
if (kr != kIOReturnSuccess)
{
print("Could not open device (error: \(kr))")
continue
}
else if (kr == kIOReturnExclusiveAccess)
{
// this is not a problem as we can still do some things
continue
}
self.connected()
}
}
private func rawDeviceRemoved(iterator: io_iterator_t) {
var kr:Int32 = 0
while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 {
// USB device object is no longer needed.
kr = IOObjectRelease(usbDevice)
if (kr != kIOReturnSuccess)
{
print("Couldn’t release raw device object (error: \(kr))")
continue
}
self.disconnected()
}
}
func getStatus() throws -> [UInt8] {
guard let deviceInterface = self.deviceInterfacePtrPtr?.pointee?.pointee else {
throw DFUDeviceError.DeviceInterfaceNotFound
}
var kr:Int32 = 0
let length:Int = 6
var requestPtr:[UInt8] = [UInt8](repeating: 0, count: length)
var request = IOUSBDevRequest(bmRequestType: USBmakebmRequestType(direction: kUSBIn, type: kUSBDevice, recipient: kUSBStandard),
bRequest: DFUREQUEST.GETSTATUS.rawValue,
wValue: 0,
wIndex: 0,
wLength: UInt16(length),
pData: &requestPtr,
wLenDone: 255)
kr = deviceInterface.DeviceRequest(self.deviceInterfacePtrPtr, &request)
if (kr != kIOReturnSuccess) {
throw DFUDeviceError.RequestError(desc: "Get device status request error: \(kr)")
}
return requestPtr
}
private func configureDevice() -> Int32 {
var kr:Int32 = 0
guard let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
print("Unable to get Device Interface")
return -1
}
var numConfig:UInt8 = 0
kr = deviceInterface.GetNumberOfConfigurations(deviceInterfacePtrPtr, &numConfig)
if numConfig == 0 {
print("Device Number Of Configurations: 0")
return -1
}
var configPtr:IOUSBConfigurationDescriptorPtr?
// set first configuration as active
kr = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr)
if (kr != kIOReturnSuccess)
{
print("Couldn’t get configuration descriptor for index (error: %x)\n", kr);
return -1
}
guard let config = configPtr?.pointee else {
return -1
}
//Set the device’s configuration. The configuration value is found in
//the bConfigurationValue field of the configuration descriptor
kr = deviceInterface.SetConfiguration(deviceInterfacePtrPtr, config.bConfigurationValue)
if (kr != kIOReturnSuccess)
{
print("Couldn’t set configuration to value (error: %x)\n", kr);
return -1
}
return kIOReturnSuccess
}
func connected() {
NotificationCenter.default.post(name: .dfuDeviceConnected, object: nil)
globalLogPost("DFU device has been device connected")
}
func disconnected() {
NotificationCenter.default.post(name: .dfuDeviceDisconnected, object: nil)
globalLogPost("DFU device has been disconnected")
}
func initUsb() {
var matchedIterator:io_iterator_t = 0
var removalIterator:io_iterator_t = 0
let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector"))
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId)
matchingDict[kUSBProductID] = NSNumber(value: self.productId)
let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.rawDeviceAdded(iterator: iterator)
}
let removalCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let this = Unmanaged<DFUDevice>
.fromOpaque(userData!).takeUnretainedValue()
this.rawDeviceRemoved(iterator: iterator)
}
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator)
IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator)
self.rawDeviceAdded(iterator: matchedIterator)
self.rawDeviceRemoved(iterator: removalIterator)
RunLoop.current.run()
}
}
You can look on method getStatus where i create a USBRequest and send it to device. Then in requestPtr:[UInt8] i received answer from device. Thank you for helping guys.
We can use ore device pointer anywhere in project, for example:
func upload(value:UInt16, length:UInt16) throws -> [UInt8] {
guard let deviceInterface = DFUDevice.sharedInstance.deviceInterfacePtrPtr?.pointee?.pointee else {
throw DFUDeviceError.DeviceInterfaceNotFound
}
var kr:Int32 = 0
var requestPtr:[UInt8] = [UInt8](repeating: 0, count: Int(length))
var request = IOUSBDevRequest(bmRequestType: 161,
bRequest: DFUREQUEST.UPLOAD.rawValue,
wValue: value,
wIndex: 0,
wLength: length,
pData: &requestPtr,
wLenDone: 255)
kr = deviceInterface.DeviceRequest(DFUDevice.sharedInstance.deviceInterfacePtrPtr, &request)
if (kr != kIOReturnSuccess) {
throw DFUDeviceError.RequestError(desc: "Upload request error: \(kr), request data: \(request)")
}
return requestPtr
}
This question already has answers here:
How to read a struct from a file in Rust?
(3 answers)
Closed 6 years ago.
I have read How to read a struct from file in Rust?, but std::slice::raw::mut_buf_as_slice has been deprecated, hence I want to re-ask this question.
I want to read a struct utmp from /var/run/utmp, and I have tried the following code:
fn read_utmp() -> Utmp {
let mut reader = BufReader::new(File::open("/var/run/utmp").unwrap());
let mut ut = vec![];
let size = mem::size_of::<Utmp>();
reader.take(size as u64).read_to_end(&mut ut);
unsafe {
std::mem::transmute(ut)
}
}
And as expected, the compiler complaint:
error: transmute called with differently sized types: std::vec::Vec (192 bits) to Utmp (3056 bits) [E0512]
How could I do this?
I believe that raw::mut_buf_as_slice was replaced with slice::from_raw_parts_mut.
Note that the following code does not take into account any endianness or padding issues and is intended to be used with POD types. struct utmp should be safe in this case.
Here is a function that can read a struct (of a pod type) from a file:
use std::io::{self, BufReader, Read};
use std::fs::{self, File};
use std::path::Path;
use std::slice;
fn read_struct<T, R: Read>(mut read: R) -> io::Result<T> {
let num_bytes = ::std::mem::size_of::<T>();
unsafe {
let mut s = ::std::mem::uninitialized();
let mut buffer = slice::from_raw_parts_mut(&mut s as *mut T as *mut u8, num_bytes);
match read.read_exact(buffer) {
Ok(()) => Ok(s),
Err(e) => {
::std::mem::forget(s);
Err(e)
}
}
}
}
// use
// read_struct::<Utmp>(reader)
If you want to read all utmp structs from the utmp file, you can execute read_struct multiple times or read all the file at once:
fn read_structs<T, P: AsRef<Path>>(path: P) -> io::Result<Vec<T>> {
let path = path.as_ref();
let struct_size = ::std::mem::size_of::<T>();
let num_bytes = try!(fs::metadata(path)).len() as usize;
let num_structs = num_bytes / struct_size;
let mut reader = BufReader::new(try!(File::open(path)));
let mut r = Vec::<T>::with_capacity(num_structs);
unsafe {
let mut buffer = slice::from_raw_parts_mut(r.as_mut_ptr() as *mut u8, num_bytes);
try!(reader.read_exact(buffer));
r.set_len(num_structs);
}
Ok(r)
}
// use
// read_structs::<Utmp, _>("/var/run/utmp"))
I'd like to remove some elements from a Vec, but vec.iter().filter().collect() creates a new vector with borrowed items.
I'd like to mutate the original Vec without extra memory allocation (and keep memory of removed elements as an extra capacity of the vector).
If you want to remove elements, you can use retain(), which removes elements from the vector if the closure returns false:
let mut vec = vec![1, 2, 3, 4];
vec.retain(|&x| x % 2 == 0);
assert_eq!(vec, [2, 4]);
If you want to modify the elements in place, you have to do that in a for x in vec.iter_mut().
If you truly want to mutate the vector's elements while filtering it, you can use the nightly-only method Vec::drain_filter, an extremely flexible tool:
#![feature(drain_filter)]
fn main() {
let mut vec = vec![1, 2, 3, 4];
vec.drain_filter(|x| {
if *x % 2 == 0 {
true
} else {
*x += 100;
false
}
});
assert_eq!(vec, [101, 103]);
}
It also allows you to get the removed elements as the return value of the entire method is an iterator!
Till Vec::drain_filter gets stable, we can solve the problem with homebrewed rust:
fn main() {
let mut v = vec![1, 2, 3, 4];
let mut i = 0;
while i < v.len() {
if v[i] % 2 == 0 {
v.remove(i);
} else {
v[i] += 100;
i += 1;
}
}
println!("{:?}", v); // [101, 103]
}
BTW remove() is an O(n) operation but doesn't allocate memory.
Playground
I am providing my take for this problem as I was unaware of the retain method:
impl<T> RemoveFilter<T> for Vec<T> {}
pub trait RemoveFilter<T>: BorrowMut<Vec<T>> {
fn remove_filter<F: for<'b> FnMut(&'b T) -> bool>(&mut self, mut cb: F) {
let vec: &mut Vec<T> = self.borrow_mut();
let mut write_to = 0;
let mut read_from = 0;
while read_from < vec.len() {
let maintain = cb(&mut vec[read_from]);
if maintain {
vec.as_mut_slice().swap(read_from, write_to);
write_to += 1;
}
read_from += 1;
}
vec.resize_with(write_to, || panic!("We are shrinking the vector"));
}
}
It will shift the elements as it iterates and then remove anything that is left behind. I think this is code may easily modified to solve other problems.