On Thu, Nov 9, 2017 at 10:37 AM, Johannes Weiß <johanneswe...@apple.com> wrote:
> Hi Kelvin, > > > On 9 Nov 2017, at 12:30 am, Kelvin Ma <kelvin1...@gmail.com> wrote: > > > > For context, the problem I’m trying to solve is efficiently parsing JPEG > chunks. This means reading each chunk of the JPEG from a file into a raw > buffer pointer, and then parsing the chunk according to its expected > layout. For example, a frame header chunk looks like this: > > > > 0 1 2 3 > 4 5 6 > > [ precision:UInt8 | height:UInt16 | > width:UInt16 | Nf:UInt8 | ... ] > > > > what I want to do is to be able to load the height and width into > something I can pass into UInt16.init(bigEndian:) without failing because > of alignment. I’ve thought of several options but none of them seem to be > great. > > > > 1 - bind the entire buffer to UInt8.self, and then do buffer[1] << > UInt8.bitWidth | buffer[2]. probably most straightforward, but doesn’t > generalize well at all to larger Int types. > > > > 2 - copy MemoryLayout<UInt16>.size bytes from offset 1 into the > beginning of a new raw buffer, aligned to MemoryLayout<UInt16>.alignment, > and do load(fromByteOffset: 0, as: UInt16.self) from that. Seems very > inefficient because you have to allocate a new heap buffer copy everything > over and then free it just to hold the bytes in the right alignment. > > > > 3 - use withUnsafeMutablePointer(to:_:) on a local variable of type > UInt16, cast it to a raw pointer, and copy MemoryLayout<UInt16>.sizebytes > into it. Like 2 it involves declaring a temporary variable which is > annoying, and also, while the default initialization isn’t that big a > problem, it’s introducing a meaningless value into the source code and can > be problematic for non-integer types. Also, wasn’t Swift supposed to be > designed so that Optional is the only thing which has a “default” value; > Bool does not default to false and Int does not default to 0. Default > constructors are evil. > > I agree. However, that meaningless value would just exist very temporarily > in a function. I think you'd need a fancier type system to express 'this is > an uninitialised value on the stack that can only be read after it has been > written to'. Sure you could use a local Int16? but that'd come with some > overhead. > > With endianness I still think you can use that function below and you'll > get it super efficient. The compiler will (likely) inline that whole > function anyway. > > What's the problem with the local temporary variable? You'd need that in C > too. Maybe can you post the C code that you'd like to write? Then we can > work from there and create some Swift code that does the same. > > > enum Endianness { > case little > case big > } > > func integerFromBuffer<T: FixedWidthInteger>(_ pointer: > UnsafeRawBufferPointer, index: Int, endianness: Endianness = .big) -> T { > precondition(index >= 0) > precondition(index <= pointer.count - MemoryLayout<T>.size) > > var value = T() > withUnsafeMutableBytes(of: &value) { valuePtr in > valuePtr.copyBytes(from: UnsafeRawBufferPointer(start: > pointer.baseAddress!.advanced(by: index), > count: > MemoryLayout<T>.size)) > } > switch endianness { > case .little: > return value.littleEndian /* does nothing on little endian, > swaps on big */ > case .big: > return value.bigEndian /* does nothing on big endian, swaps on > little */ > } > } > > -- Johannes > > Is it safe to use UnsafeRawPointer instead? func loadBigEndianInt<I>(from buffer:UnsafeRawBufferPointer, atByteOffset offset:Int) -> I where I:FixedWidthInteger { var i = I() withUnsafeMutablePointer(to: &i) { UnsafeMutableRawPointer($0).copyBytes(from: buffer.baseAddress! + offset, count: MemoryLayout<I>.size) } return I(bigEndian: i) }
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users