Proper syntax for inout array handling?


(Ken Burgett) #1

I am converting a very dirty C program to Swift3, and their is plenty of pointer arithmetic to be dealt with. As part of the effort, I have created a 'memcpy clone to help with some of the transformation.
Here is the output from the REPL:

···

================================
   1. var source = [UInt8](repeating: 0x1f, count: 32)
   2. var destination = [UInt8](repeating: 0, count: 64)
   3.
   4. func memcpy(dest: inout [UInt8], src: inout [UInt8], count: Int)
   5. {
   6. for ix in 0...count-1
   7. {
   8. dest[ix] = src[ix]
   9. }
  10. }
  11.
  12. memcpy(dest: &destination, src: &source, count: 32 )
  13.
  14. memcpy(dest: &destination[3], src: &source, count: 13)
error: repl.swift:14:26: error: cannot convert value of type 'UInt8' to expected argument type '[UInt8]'
memcpy(dest: &destination[3], src: &source, count: 13)

which shows me that the compiler does not like the form of &destination[13], and doesn't treat it as a [UInt8].

What is the correct syntax for using a base + offset as a source or destination for a memory copy?

--
Ken Burgett
Principal Software Engineer
Email: kenb@iotone.io
Office: 530.693.4449
Mobile: 831.332.6846
URL: www.iotone.co


(Saagar Jha) #2

Swift handling of Arrays isn’t like C (for which you can use UnsafePointer)-you
don’t access the pointer of the third element. Instead, you can use Array
slices, which makes it a one liner:

destination[destinationStartIndex..<destinationStartIndex + count] =
source[sourceStartIndex..<sourceStartIndex + count]

In your case,

destination[0..<0 + 32] = source[0..<0 + 32]
destination[3..3 + 13] = source[0..<0 + 13]

As a function:

func memcpy(source: [UInt8], destination: inout [UInt8],
sourceStartIndex: Int = 0, destinationStartIndex: Int = 0, count: Int)
{
    destination[destinationStartIndex..<destinationStartIndex + count]
= source[sourceStartIndex..<sourceStartIndex + count]
}

memcpy(source: source, destination: &destination, count: 32)

memcpy(source: source, destination: &destination,
destinationStartIndex: 3, count: 13)

···

On Thu, Jun 9, 2016 at 1:38 PM Ken Burgett via swift-users < swift-users@swift.org> wrote:

I am converting a very dirty C program to Swift3, and their is plenty of
pointer arithmetic to be dealt with. As part of the effort, I have
created a 'memcpy clone to help with some of the transformation.
Here is the output from the REPL:

   1. var source = [UInt8](repeating: 0x1f, count: 32)
   2. var destination = [UInt8](repeating: 0, count: 64)
   3.
   4. func memcpy(dest: inout [UInt8], src: inout [UInt8], count: Int)
   5. {
   6. for ix in 0...count-1
   7. {
   8. dest[ix] = src[ix]
   9. }
  10. }
  11.
  12. memcpy(dest: &destination, src: &source, count: 32 )
  13.
  14. memcpy(dest: &destination[3], src: &source, count: 13)
error: repl.swift:14:26: error: cannot convert value of type 'UInt8' to
expected argument type '[UInt8]'
memcpy(dest: &destination[3], src: &source, count: 13)

which shows me that the compiler does not like the form of
&destination[13], and doesn't treat it as a [UInt8].

What is the correct syntax for using a base + offset as a source or
destination for a memory copy?

--
Ken Burgett
Principal Software Engineer
Email: kenb@iotone.io
Office: 530.693.4449
Mobile: 831.332.6846
URL: www.iotone.co
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
-Saagar Jha


(Saagar Jha) #3

Nevermind, I lied. Swift does allow direct pointer arithmetic:

import Foundation

var source = [UInt8](repeating: 0x1f, count: 32)
var destination = [UInt8](repeating: 0, count: 64)

memcpy(&destination, source, 32) // the C function

memcpy(&destination + 3, source, 13) // the + operator works to offset

···

On Thu, Jun 9, 2016 at 7:21 PM Saagar Jha <saagarjha28@gmail.com> wrote:

Swift handling of Arrays isn’t like C (for which you can use UnsafePointer)-you
don’t access the pointer of the third element. Instead, you can use Array
slices, which makes it a one liner:

destination[destinationStartIndex..<destinationStartIndex + count] = source[sourceStartIndex..<sourceStartIndex + count]

In your case,

destination[0..<0 + 32] = source[0..<0 + 32]
destination[3..3 + 13] = source[0..<0 + 13]

As a function:

func memcpy(source: [UInt8], destination: inout [UInt8], sourceStartIndex: Int = 0, destinationStartIndex: Int = 0, count: Int) {
    destination[destinationStartIndex..<destinationStartIndex + count] = source[sourceStartIndex..<sourceStartIndex + count]
}

memcpy(source: source, destination: &destination, count: 32)

memcpy(source: source, destination: &destination, destinationStartIndex: 3, count: 13)

On Thu, Jun 9, 2016 at 1:38 PM Ken Burgett via swift-users < > swift-users@swift.org> wrote:

I am converting a very dirty C program to Swift3, and their is plenty of
pointer arithmetic to be dealt with. As part of the effort, I have
created a 'memcpy clone to help with some of the transformation.
Here is the output from the REPL:

   1. var source = [UInt8](repeating: 0x1f, count: 32)
   2. var destination = [UInt8](repeating: 0, count: 64)
   3.
   4. func memcpy(dest: inout [UInt8], src: inout [UInt8], count: Int)
   5. {
   6. for ix in 0...count-1
   7. {
   8. dest[ix] = src[ix]
   9. }
  10. }
  11.
  12. memcpy(dest: &destination, src: &source, count: 32 )
  13.
  14. memcpy(dest: &destination[3], src: &source, count: 13)
error: repl.swift:14:26: error: cannot convert value of type 'UInt8' to
expected argument type '[UInt8]'
memcpy(dest: &destination[3], src: &source, count: 13)

which shows me that the compiler does not like the form of
&destination[13], and doesn't treat it as a [UInt8].

What is the correct syntax for using a base + offset as a source or
destination for a memory copy?

--
Ken Burgett
Principal Software Engineer
Email: kenb@iotone.io
Office: 530.693.4449
Mobile: 831.332.6846
URL: www.iotone.co
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
-Saagar Jha

--
-Saagar Jha


(Joe Groff) #4

Arrays can indeed be used as pointer parameters, but the second one only works by accident. The pointer bridging has the same semantics as an 'inout' parameter, so the pointer is only valid for the duration of the immediate call, and since operators in Swift are also function calls, the pointer expires after the '+' operation. If you're doing anything with an array other than passing it off to a single C function, you should use withUnsafeMutableBufferPointer instead:

destination.withUnsafeMutableBufferPointer { p in
  memcpy(p.baseAddress, source, 32)
  memcpy(p.baseAddress + 3, source, 13)
}

In addition to not having undefined behavior, this will also probably be faster, since it'll only need to pin the array for pointer access once instead of twice.

-Joe

···

On Jun 9, 2016, at 8:39 PM, Saagar Jha via swift-users <swift-users@swift.org> wrote:

Nevermind, I lied. Swift does allow direct pointer arithmetic:

import Foundation

var source = [UInt8](repeating: 0x1f, count: 32)
var destination = [UInt8](repeating: 0, count: 64)

memcpy(&destination, source, 32) // the C function

memcpy(&destination + 3, source, 13) // the + operator works to offset


(Ken Burgett) #5

Thanks for the good answers, both Saager and Joe. I like the way you both developed solutions and then improved them, and for good reasons.

I do have a situation in the ugly C code I am porting to Swift where a pointer gets passed down to a second function, so Joe's solution looks like the right approach.

Thanks again.

···

On 2016-06-09 21:37, Joe Groff wrote:

On Jun 9, 2016, at 8:39 PM, Saagar Jha via swift-users >> <swift-users@swift.org> wrote:

Nevermind, I lied. Swift does allow direct pointer arithmetic:

import Foundation

var source = [UInt8](repeating: 0x1f, count: 32)
var destination = [UInt8](repeating: 0, count: 64)

memcpy(&destination, source, 32) // the C function

memcpy(&destination + 3, source, 13) // the + operator works to offset

Arrays can indeed be used as pointer parameters, but the second one
only works by accident. The pointer bridging has the same semantics as
an 'inout' parameter, so the pointer is only valid for the duration of
the immediate call, and since operators in Swift are also function
calls, the pointer expires after the '+' operation. If you're doing
anything with an array other than passing it off to a single C
function, you should use withUnsafeMutableBufferPointer instead:

destination.withUnsafeMutableBufferPointer { p in
  memcpy(p.baseAddress, source, 32)
  memcpy(p.baseAddress + 3, source, 13)
}

In addition to not having undefined behavior, this will also probably
be faster, since it'll only need to pin the array for pointer access
once instead of twice.

-Joe

--
Ken Burgett
Principal Software Engineer
Email: kenb@iotone.io
Office: 530.693.4449
Mobile: 831.332.6846
URL: www.iotone.co