Extracting arbitrary types (e.g. UInt16) out of Data


(Rick M) #1

I continue to struggle with the "proper" and most efficient way to do things with Data.

In this case, I have a set of bytes received over a serial port in a Data. The last two bytes are a (big- or little-endian) UInt16 CRC. However, there maybe an odd or even number of bytes in the Data before these last two bytes, so I can't just use withUnsafePointer<UInt16>.

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

If the endianness needs to change, I can do that swapping after I've gotten the typed value out.

···

--
Rick Mann
rmann@latencyzero.com


(Philippe Hausler) #2

There are probably a number of ways that would do what you need. I would need a bit more context or examples of what you are doing already to comment. But if I had those parameters to work with I would use copyBytes into the address of the target you are wanting to read.

There are some cases that might be improved when we add the UnsafeRawBuferPointer apis to Data.

Can you share a small sample of what you have already?

···

On Jun 25, 2017, at 5:37 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I continue to struggle with the "proper" and most efficient way to do things with Data.

In this case, I have a set of bytes received over a serial port in a Data. The last two bytes are a (big- or little-endian) UInt16 CRC. However, there maybe an odd or even number of bytes in the Data before these last two bytes, so I can't just use withUnsafePointer<UInt16>.

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

If the endianness needs to change, I can do that swapping after I've gotten the typed value out.

--
Rick Mann
rmann@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Daniel Vollmer) #3

Hi Rick,

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).

So at least IMO, copying and assembling the bytes into an instance of the actual destination type seems to be exactly the right thing to do.

  Daniel.

···

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org> wrote:


(Rick M) #4

I mean, it's as straightforward as my example. I have a Data of arbitrary size (anywhere from 3 to 29 bytes, let's say). The last two bytes form a UInt16 CRC. I need to get those last two out and compare them against the CRC I compute for the rest of the bytes.

Having said that, I just used withUnsafeBytes() and grabbed the last two bytes, and assembled them into a UInt16 with shift and or.

I'd like to be able to do something like value<Double>(at: 3), though.

···

On Jun 25, 2017, at 19:53 , Philippe Hausler <phausler@apple.com> wrote:

There are probably a number of ways that would do what you need. I would need a bit more context or examples of what you are doing already to comment. But if I had those parameters to work with I would use copyBytes into the address of the target you are wanting to read.

There are some cases that might be improved when we add the UnsafeRawBuferPointer apis to Data.

Can you share a small sample of what you have already?

On Jun 25, 2017, at 5:37 PM, Rick Mann via swift-users <swift-users@swift.org> wrote:

I continue to struggle with the "proper" and most efficient way to do things with Data.

In this case, I have a set of bytes received over a serial port in a Data. The last two bytes are a (big- or little-endian) UInt16 CRC. However, there maybe an odd or even number of bytes in the Data before these last two bytes, so I can't just use withUnsafePointer<UInt16>.

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

If the endianness needs to change, I can do that swapping after I've gotten the typed value out.

--
Rick Mann
rmann@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
Rick Mann
rmann@latencyzero.com


(Joe Groff) #5

Unaligned memory accesses are not currently allowed by the language semantics, regardless of the underlying ISA. You should use memcpy if you need to load potentially-unaligned values out of raw memory.

-Joe

···

On Jun 26, 2017, at 1:55 AM, Daniel Vollmer via swift-users <swift-users@swift.org> wrote:

Hi Rick,

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org> wrote:

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).


(Philippe Hausler) #6

Data.copyBytes will do that under the hood

var crc: UInt16 = 0
let amountCopied = withUnsafeMutablePointer(to: &crc) { data.copyBytes(to: UnsafeMutableBufferPointer(start: $0, count: 1)) }
if amountCopied == MemoryLayout<UInt16>.size {
    // we have a full crc
}

That will probably do what you want; plus it will allow you to do it from a given range of bytes.

···

On Jun 26, 2017, at 9:57 AM, Joe Groff via swift-users <swift-users@swift.org> wrote:

On Jun 26, 2017, at 1:55 AM, Daniel Vollmer via swift-users <swift-users@swift.org> wrote:

Hi Rick,

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org> wrote:

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).

Unaligned memory accesses are not currently allowed by the language semantics, regardless of the underlying ISA. You should use memcpy if you need to load potentially-unaligned values out of raw memory.

-Joe
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Charles Srstka) #7

While that’s true, the bindMemory() function takes as arguments not only the type you’re binding to, but also the number of objects you plan to read. So if Swift is ever ported to a platform that doesn’t allow unaligned access (I don’t think it is currently, although I could be wrong about that), it wouldn’t be difficult to make the version of bindMemory() for that architecture copy the bytes to a safe address in the case that the underlying memory isn’t properly aligned for that type.

Charles

···

On Jun 26, 2017, at 3:55 AM, Daniel Vollmer via swift-users <swift-users@swift.org> wrote:

Hi Rick,

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).

So at least IMO, copying and assembling the bytes into an instance of the actual destination type seems to be exactly the right thing to do.

  Daniel.


(Joe Groff) #8

Cool. That'd be a nice API to backport to *BufferPointer too!

-Joe

···

On Jun 26, 2017, at 10:05 AM, Philippe Hausler <phausler@apple.com> wrote:

Data.copyBytes will do that under the hood

var crc: UInt16 = 0
let amountCopied = withUnsafeMutablePointer(to: &crc) { data.copyBytes(to: UnsafeMutableBufferPointer(start: $0, count: 1)) }
if amountCopied == MemoryLayout<UInt16>.size {
   // we have a full crc
}

That will probably do what you want; plus it will allow you to do it from a given range of bytes.


(Charles Srstka) #9

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.

In that case, the simplest way to do it is probably just this:

let crc = (UInt16(myData[myData.endIndex]) << 8) | UInt16(myData[myData.endIndex - 1])

(or the reverse, depending on the endianness of the source data)

Charles

···

On Jun 26, 2017, at 12:05 PM, Philippe Hausler via swift-users <swift-users@swift.org> wrote:

Data.copyBytes will do that under the hood

var crc: UInt16 = 0
let amountCopied = withUnsafeMutablePointer(to: &crc) { data.copyBytes(to: UnsafeMutableBufferPointer(start: $0, count: 1)) }
if amountCopied == MemoryLayout<UInt16>.size {
   // we have a full crc
}

That will probably do what you want; plus it will allow you to do it from a given range of bytes.

On Jun 26, 2017, at 9:57 AM, Joe Groff via swift-users <swift-users@swift.org> wrote:

On Jun 26, 2017, at 1:55 AM, Daniel Vollmer via swift-users <swift-users@swift.org> wrote:

Hi Rick,

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org> wrote:

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).

Unaligned memory accesses are not currently allowed by the language semantics, regardless of the underlying ISA. You should use memcpy if you need to load potentially-unaligned values out of raw memory.

-Joe
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Charles Srstka) #10

You kinda can, although it’s a bit more verbose:

let crc = myData[(myData.endIndex - 2)…].withUnsafeBytes { UInt16(littleEndian: $0.pointee) }

If you look at the source, withUnsafeBytes calls withMemoryRebound with the inferred generic type (UInt16 in this case), so I think this should be safe.

Charles

···

On Jun 26, 2017, at 12:00 AM, Roderick Mann via swift-users <swift-users@swift.org> wrote:

I mean, it's as straightforward as my example. I have a Data of arbitrary size (anywhere from 3 to 29 bytes, let's say). The last two bytes form a UInt16 CRC. I need to get those last two out and compare them against the CRC I compute for the rest of the bytes.

Having said that, I just used withUnsafeBytes() and grabbed the last two bytes, and assembled them into a UInt16 with shift and or.

I'd like to be able to do something like value<Double>(at: 3), though.


(Philippe Hausler) #11

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.

In that case, the simplest way to do it is probably just this:

let crc = (UInt16(myData[myData.endIndex]) << 8) | UInt16(myData[myData.endIndex - 1])

Yea that should work; and should be really fast as well. My example was just funneling it to memcpy and give you are guard where the bytes were not fully initialized.

···

On Jun 26, 2017, at 10:20 AM, Charles Srstka <cocoadev@charlessoft.com> wrote:

(or the reverse, depending on the endianness of the source data)

Charles

On Jun 26, 2017, at 12:05 PM, Philippe Hausler via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Data.copyBytes will do that under the hood

var crc: UInt16 = 0
let amountCopied = withUnsafeMutablePointer(to: &crc) { data.copyBytes(to: UnsafeMutableBufferPointer(start: $0, count: 1)) }
if amountCopied == MemoryLayout<UInt16>.size {
   // we have a full crc
}

That will probably do what you want; plus it will allow you to do it from a given range of bytes.

On Jun 26, 2017, at 9:57 AM, Joe Groff via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

On Jun 26, 2017, at 1:55 AM, Daniel Vollmer via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hi Rick,

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).

Unaligned memory accesses are not currently allowed by the language semantics, regardless of the underlying ISA. You should use memcpy if you need to load potentially-unaligned values out of raw memory.

-Joe
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Joe Groff) #12

There are restrictions that unfortunately prevent unaligned memory support from being the pervasive default; particularly, we want typed pointers to be able to "toll-free-bridge" with typed C pointers, and C also requires well-alignedness of typed memory accesses. Swift's runtime generics model also means that unspecialized code would be using value witness functions to load/store values from memory, and the value witness functions are compiled to assume alignment of their arguments. It would be reasonable for us to add load/storeUnaligned APIs to the RawPointer types that explicitly did unaligned operations; these would however have to be restricted to working only on trivial types that don't require reference counting.

-Joe

···

On Jun 26, 2017, at 10:20 AM, Charles Srstka via swift-users <swift-users@swift.org> wrote:

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.


(Charles Srstka) #13

Restricting it to trivial types wouldn’t be bad at all, since stuff you’re reading/writing from raw data is generally going to be either 1) trivial types, or 2) Codable types whose encode/decode routines are usually going to read and write a bunch of... trivial types.

Just reading a whole struct as a chunk of memory has never seemed like a good idea to me, if for no other reason than that it’ll only work if the data happens to be in the same endian order as the host.

Charles

···

On Jun 26, 2017, at 12:58 PM, Joe Groff <jgroff@apple.com> wrote:

On Jun 26, 2017, at 10:20 AM, Charles Srstka via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.

There are restrictions that unfortunately prevent unaligned memory support from being the pervasive default; particularly, we want typed pointers to be able to "toll-free-bridge" with typed C pointers, and C also requires well-alignedness of typed memory accesses. Swift's runtime generics model also means that unspecialized code would be using value witness functions to load/store values from memory, and the value witness functions are compiled to assume alignment of their arguments. It would be reasonable for us to add load/storeUnaligned APIs to the RawPointer types that explicitly did unaligned operations; these would however have to be restricted to working only on trivial types that don't require reference counting.

-Joe


(Rick M) #14

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.

In that case, the simplest way to do it is probably just this:

let crc = (UInt16(myData[myData.endIndex]) << 8) | UInt16(myData[myData.endIndex - 1])

(or the reverse, depending on the endianness of the source data)

Ah. I was doing it like this, but I guess I don't really need to, do I?

    let count = self.count
    let ourCRC = self.withUnsafeBytes
    { (inPtr: UnsafePointer<UInt8>) -> UInt16 in
        let b1 = UInt16(inPtr[count - 2])
        let b2 = UInt16(inPtr[count - 1])
        let b = (b1 << 8) | b2
        return b
    }

···

On Jun 26, 2017, at 10:20 , Charles Srstka <cocoadev@charlessoft.com> wrote:

Charles

On Jun 26, 2017, at 12:05 PM, Philippe Hausler via swift-users <swift-users@swift.org> wrote:

Data.copyBytes will do that under the hood

var crc: UInt16 = 0
let amountCopied = withUnsafeMutablePointer(to: &crc) { data.copyBytes(to: UnsafeMutableBufferPointer(start: $0, count: 1)) }
if amountCopied == MemoryLayout<UInt16>.size {
   // we have a full crc
}

That will probably do what you want; plus it will allow you to do it from a given range of bytes.

On Jun 26, 2017, at 9:57 AM, Joe Groff via swift-users <swift-users@swift.org> wrote:

On Jun 26, 2017, at 1:55 AM, Daniel Vollmer via swift-users <swift-users@swift.org> wrote:

Hi Rick,

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org> wrote:

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).

Unaligned memory accesses are not currently allowed by the language semantics, regardless of the underlying ISA. You should use memcpy if you need to load potentially-unaligned values out of raw memory.

-Joe
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
Rick Mann
rmann@latencyzero.com


(Rick M) #15

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.

In that case, the simplest way to do it is probably just this:

let crc = (UInt16(myData[myData.endIndex]) << 8) | UInt16(myData[myData.endIndex - 1])

By the way, self.endIndex == self.count, so shouldn't these both have an additional 1 subtracted?

That's what I'm seeing, and what the docs show. What's the point of endIndex? Completeness?

···

On Jun 26, 2017, at 10:20 , Charles Srstka <cocoadev@charlessoft.com> wrote:

(or the reverse, depending on the endianness of the source data)

Charles

On Jun 26, 2017, at 12:05 PM, Philippe Hausler via swift-users <swift-users@swift.org> wrote:

Data.copyBytes will do that under the hood

var crc: UInt16 = 0
let amountCopied = withUnsafeMutablePointer(to: &crc) { data.copyBytes(to: UnsafeMutableBufferPointer(start: $0, count: 1)) }
if amountCopied == MemoryLayout<UInt16>.size {
   // we have a full crc
}

That will probably do what you want; plus it will allow you to do it from a given range of bytes.

On Jun 26, 2017, at 9:57 AM, Joe Groff via swift-users <swift-users@swift.org> wrote:

On Jun 26, 2017, at 1:55 AM, Daniel Vollmer via swift-users <swift-users@swift.org> wrote:

Hi Rick,

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org> wrote:

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).

Unaligned memory accesses are not currently allowed by the language semantics, regardless of the underlying ISA. You should use memcpy if you need to load potentially-unaligned values out of raw memory.

-Joe
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
Rick Mann
rmann@latencyzero.com


(Charles Srstka) #16

Self-correction: It calls bindMemory, not withMemoryRebound. Same idea, though.

Charles

···

On Jun 26, 2017, at 1:11 AM, Charles Srstka via swift-users <swift-users@swift.org> wrote:

On Jun 26, 2017, at 12:00 AM, Roderick Mann via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

I mean, it's as straightforward as my example. I have a Data of arbitrary size (anywhere from 3 to 29 bytes, let's say). The last two bytes form a UInt16 CRC. I need to get those last two out and compare them against the CRC I compute for the rest of the bytes.

Having said that, I just used withUnsafeBytes() and grabbed the last two bytes, and assembled them into a UInt16 with shift and or.

I'd like to be able to do something like value<Double>(at: 3), though.

You kinda can, although it’s a bit more verbose:

let crc = myData[(myData.endIndex - 2)…].withUnsafeBytes { UInt16(littleEndian: $0.pointee) }

If you look at the source, withUnsafeBytes calls withMemoryRebound with the inferred generic type (UInt16 in this case), so I think this should be safe.

Charles


(Rick M) #17

Thanks, I'll give that a try.

···

On Jun 25, 2017, at 23:11 , Charles Srstka <cocoadev@charlessoft.com> wrote:

On Jun 26, 2017, at 12:00 AM, Roderick Mann via swift-users <swift-users@swift.org> wrote:

I mean, it's as straightforward as my example. I have a Data of arbitrary size (anywhere from 3 to 29 bytes, let's say). The last two bytes form a UInt16 CRC. I need to get those last two out and compare them against the CRC I compute for the rest of the bytes.

Having said that, I just used withUnsafeBytes() and grabbed the last two bytes, and assembled them into a UInt16 with shift and or.

I'd like to be able to do something like value<Double>(at: 3), though.

You kinda can, although it’s a bit more verbose:

let crc = myData[(myData.endIndex - 2)…].withUnsafeBytes { UInt16(littleEndian: $0.pointee) }

If you look at the source, withUnsafeBytes calls withMemoryRebound with the inferred generic type (UInt16 in this case), so I think this should be safe.

Charles

--
Rick Mann
rmann@latencyzero.com


(Philippe Hausler) #18

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.

In that case, the simplest way to do it is probably just this:

let crc = (UInt16(myData[myData.endIndex]) << 8) | UInt16(myData[myData.endIndex - 1])

By the way, self.endIndex == self.count, so shouldn't these both have an additional 1 subtracted?

That's what I'm seeing, and what the docs show. What's the point of endIndex? Completeness?

If the data was sliced.

e.g.

let d = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
let slice = d[2..<4]

slice.endIndex != slice.count

···

On Jun 26, 2017, at 1:47 PM, Roderick Mann <rmann@latencyzero.com> wrote:

On Jun 26, 2017, at 10:20 , Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

(or the reverse, depending on the endianness of the source data)

Charles

On Jun 26, 2017, at 12:05 PM, Philippe Hausler via swift-users <swift-users@swift.org> wrote:

Data.copyBytes will do that under the hood

var crc: UInt16 = 0
let amountCopied = withUnsafeMutablePointer(to: &crc) { data.copyBytes(to: UnsafeMutableBufferPointer(start: $0, count: 1)) }
if amountCopied == MemoryLayout<UInt16>.size {
  // we have a full crc
}

That will probably do what you want; plus it will allow you to do it from a given range of bytes.

On Jun 26, 2017, at 9:57 AM, Joe Groff via swift-users <swift-users@swift.org> wrote:

On Jun 26, 2017, at 1:55 AM, Daniel Vollmer via swift-users <swift-users@swift.org> wrote:

Hi Rick,

On 26. Jun 2017, at 02:37, Rick Mann via swift-users <swift-users@swift.org> wrote:

[snip]

I'd also like to avoid unnecessary copying of the data. All of it is immutable for the purposes of this problem.

How can I get the UInt16 that starts at byte X in a Data? Same goes for Double or Int32 or whatever.

I’m not sure what Swift’s stance on this is, but not all platforms allow misaligned memory accesses (such as your attempt to access a UInt16 that lies at an odd memory address).

Unaligned memory accesses are not currently allowed by the language semantics, regardless of the underlying ISA. You should use memcpy if you need to load potentially-unaligned values out of raw memory.

-Joe
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
Rick Mann
rmann@latencyzero.com <mailto:rmann@latencyzero.com>


(Charles Srstka) #19

Doing it via the pointer might be a smidge faster, but I kinda doubt you’ll notice it.

Charles

···

On Jun 26, 2017, at 3:41 PM, Roderick Mann <rmann@latencyzero.com> wrote:

On Jun 26, 2017, at 10:20 , Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.

In that case, the simplest way to do it is probably just this:

let crc = (UInt16(myData[myData.endIndex]) << 8) | UInt16(myData[myData.endIndex - 1])

(or the reverse, depending on the endianness of the source data)

Ah. I was doing it like this, but I guess I don't really need to, do I?

   let count = self.count
   let ourCRC = self.withUnsafeBytes
   { (inPtr: UnsafePointer<UInt8>) -> UInt16 in
       let b1 = UInt16(inPtr[count - 2])
       let b2 = UInt16(inPtr[count - 1])
       let b = (b1 << 8) | b2
       return b
   }


(Charles Srstka) #20

Rats, I was hoping that one of the reasons about being so explicit what we’re going to access and where with bindMemory() and friends would be to take care of these sorts of issues.

In that case, the simplest way to do it is probably just this:

let crc = (UInt16(myData[myData.endIndex]) << 8) | UInt16(myData[myData.endIndex - 1])

By the way, self.endIndex == self.count, so shouldn't these both have an additional 1 subtracted?

Whoops, you’re right.

That's what I'm seeing, and what the docs show. What's the point of endIndex? Completeness?

Short answer: Always use startIndex and endIndex, never, ever, *ever* use 0 or count.

Long answer:

- - - - -

import Foundation

let data = Data(bytes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
let slice = data[4..<8]

let firstTwo = slice[0..<2]
let lastTwo = slice[(slice.count - 2)..<slice.count]

print("firstTwo: \(firstTwo.map { $0 }), lastTwo: \(lastTwo.map { $0 })”)

- - - - -

What do you think the above will output?

If you said “[4, 5], then [6, 7]”, you’re wrong. On my machine, this outputs:

- - - - -

firstTwo: [0, 1], lastTwo: [4, 5]

- - - - -

Why the heck did that happen, you ask? Well, it’s because Data is its own slice, which means a Data made from another Data retains the indices *of its parent Data*, not of the slice. So when you called slice[0..<2], it returned the first two bytes *of the parent data*, which are 0 and 1. You might be wondering why it returned 4 and 5 for the last two bytes when those aren’t the 2nd and 3rd byte of either collection… I think it’s just because these out-of-range accesses are undefined behavior (on the latest version of the compiler from GitHub, these all crash).

Anyway, doing it with startIndex and endIndex actually gets you the values you expect.

Charles

···

On Jun 26, 2017, at 3:47 PM, Roderick Mann <rmann@latencyzero.com> wrote:

On Jun 26, 2017, at 10:20 , Charles Srstka <cocoadev@charlessoft.com> wrote: