[swift-users] raw buffer pointer load alignment

Kelvin Ma kelvin13ma at gmail.com
Thu Nov 9 19:24:47 CST 2017


On Thu, Nov 9, 2017 at 10:37 AM, Johannes Weiß <johannesweiss at apple.com>
wrote:

> Hi Kelvin,
>
> > On 9 Nov 2017, at 12:30 am, Kelvin Ma <kelvin13ma at 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)
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20171109/0a75411f/attachment.html>


More information about the swift-users mailing list