Java ByteBuffer equivalent in Swift 4

Viewed 2538

I'm trying to convert my underneath Java (Android) code to Swift. I'm stuck at the part of the ByteBuffer.

private MqttMessage createMqttMessage(boolean encrypted, byte[] message) {
    /* add a zero-byte at the end just to be sure it's terminated although the payload is zero-padded */
    int padding = 16 - ((message.length) % 16);

    /* a zero-byte at the end of the JSON is required for determining the string's end */
    padding = padding == 0 ? 16 : padding;

    byte[] payload = new byte[message.length+padding];

    System.arraycopy(message, 0, payload, 0, message.length);

    /* FRAME_HEADER + JSON-payload + zero-byte + FRAME_TAIL_SIZE */
    int bufferSize = Constants.FRAME_HEADER_SIZE + payload.length + 1 + Constants.FRAME_TAIL_SIZE;

    ByteBuffer frame = ByteBuffer.allocate(bufferSize);

    /* The CC3220 is an ARM device, thus little-endian */
    frame.order(ByteOrder.LITTLE_ENDIAN);

    // message flags (uint8_t)
    byte flags = (byte) (encrypted ? Constants.FRAME_FLAG_ENCRYPTED : 0x0);
    // always use authentication
    flags |= Constants.FRAME_FLAG_AUTHENTICATED;

    frame.put(flags);

    // User token   (uint32_t)
    frame.putInt((int)this.mUserToken);

What's the equivalent of a ByteBuffer in Swift?

This is what I have so far:

private func createMqttMessage(encrypted: Bool, message: [Int8]) {

    var padding:Int = 16 - ((message.count) % 16)

    padding = padding == 0 ? 16 : padding

    var payload:[Int8]

    payload = Array(message[0..<0+message.count])

    let bufferSize = Constants.FRAME_HEADER_SIZE + payload.count + 1 + Constants.FRAME_TAIL_SIZE
2 Answers

You can use this for ByteBuffer.. it behaves like the Java one.. Some functions might be missing but it should be easy to add them.

import Foundation

public class ByteBuffer {

    public init(size: Int) {
        array.reserveCapacity(size)
    }

    public func allocate(_ size: Int) {
        array = [UInt8]()
        array.reserveCapacity(size)
        currentIndex = 0
    }

    public func nativeByteOrder() -> Endianness {
        return hostEndianness
    }

    public func currentByteOrder() -> Endianness {
        return currentEndianness
    }

    public func order(_ endianness: Endianness) -> ByteBuffer {
        currentEndianness = endianness
        return self
    }

    public func put(_ value: UInt8) -> ByteBuffer {
        array.append(value)
        return self
    }

    public func put(_ value: Int32) -> ByteBuffer {
        if currentEndianness == .little {
            array.append(contentsOf: to(value.littleEndian))
            return self
        }

        array.append(contentsOf: to(value.bigEndian))
        return self
    }

    public func put(_ value: Int64) -> ByteBuffer {
        if currentEndianness == .little {
            array.append(contentsOf: to(value.littleEndian))
            return self
        }

        array.append(contentsOf: to(value.bigEndian))
        return self
    }

    public func put(_ value: Int) -> ByteBuffer {
        if currentEndianness == .little {
            array.append(contentsOf: to(value.littleEndian))
            return self
        }

        array.append(contentsOf: to(value.bigEndian))
        return self
    }

    public func put(_ value: Float) -> ByteBuffer {
        if currentEndianness == .little {
            array.append(contentsOf: to(value.bitPattern.littleEndian))
            return self
        }

        array.append(contentsOf: to(value.bitPattern.bigEndian))
        return self
    }

    public func put(_ value: Double) -> ByteBuffer {
        if currentEndianness == .little {
            array.append(contentsOf: to(value.bitPattern.littleEndian))
            return self
        }

        array.append(contentsOf: to(value.bitPattern.bigEndian))
        return self
    }

    public func get() -> UInt8 {
        let result = array[currentIndex]
        currentIndex += 1
        return result
    }

    public func get(_ index: Int) -> UInt8 {
        return array[index]
    }

    public func getInt32() -> Int32 {
        let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<Int32>.size]), Int32.self)
        currentIndex += MemoryLayout<Int32>.size
        return currentEndianness == .little ? result.littleEndian : result.bigEndian
    }

    public func getInt32(_ index: Int) -> Int32 {
        let result = from(Array(array[index..<index + MemoryLayout<Int32>.size]), Int32.self)
        return currentEndianness == .little ? result.littleEndian : result.bigEndian
    }

    public func getInt64() -> Int64 {
        let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<Int64>.size]), Int64.self)
        currentIndex += MemoryLayout<Int64>.size
        return currentEndianness == .little ? result.littleEndian : result.bigEndian
    }

    public func getInt64(_ index: Int) -> Int64 {
        let result = from(Array(array[index..<index + MemoryLayout<Int64>.size]), Int64.self)
        return currentEndianness == .little ? result.littleEndian : result.bigEndian
    }

    public func getInt() -> Int {
        let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<Int>.size]), Int.self)
        currentIndex += MemoryLayout<Int>.size
        return currentEndianness == .little ? result.littleEndian : result.bigEndian
    }

    public func getInt(_ index: Int) -> Int {
        let result = from(Array(array[index..<index + MemoryLayout<Int>.size]), Int.self)
        return currentEndianness == .little ? result.littleEndian : result.bigEndian
    }

    public func getFloat() -> Float {
        let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<UInt32>.size]), UInt32.self)
        currentIndex += MemoryLayout<UInt32>.size
        return currentEndianness == .little ? Float(bitPattern: result.littleEndian) : Float(bitPattern: result.bigEndian)
    }

    public func getFloat(_ index: Int) -> Float {
        let result = from(Array(array[index..<index + MemoryLayout<UInt32>.size]), UInt32.self)
        return currentEndianness == .little ? Float(bitPattern: result.littleEndian) : Float(bitPattern: result.bigEndian)
    }

    public func getDouble() -> Double {
        let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<UInt64>.size]), UInt64.self)
        currentIndex += MemoryLayout<UInt64>.size
        return currentEndianness == .little ? Double(bitPattern: result.littleEndian) : Double(bitPattern: result.bigEndian)
    }

    public func getDouble(_ index: Int) -> Double {
        let result = from(Array(array[index..<index + MemoryLayout<UInt64>.size]), UInt64.self)
        return currentEndianness == .little ? Double(bitPattern: result.littleEndian) : Double(bitPattern: result.bigEndian)
    }


    public enum Endianness {
        case little
        case big
    }

    private func to<T>(_ value: T) -> [UInt8] {
        var value = value
        return withUnsafeBytes(of: &value, Array.init)
    }

    private func from<T>(_ value: [UInt8], _: T.Type) -> T {
        return value.withUnsafeBytes {
            $0.load(fromByteOffset: 0, as: T.self)
        }
    }

    private var array = [UInt8]()
    private var currentIndex: Int = 0

    private var currentEndianness: Endianness = .big
    private let hostEndianness: Endianness = OSHostByteOrder() == OSLittleEndian ? .little : .big
}

let buffer = ByteBuffer(size: 100)
buffer.order(.big)
buffer.put(1.019001)
buffer.order(.little)
buffer.put(1005)
buffer.order(.big)
buffer.put(1005)

buffer.order(.big)
print(buffer.getDouble())
buffer.order(.little)
print(buffer.getInt())
buffer.order(.big)
print(buffer.getInt())

You can use Data class as a buffer

var frame = Data(capacity: bufferSize)

but you don't actually need to specify the capacity, and you can wait with initialization of frame until you actually have any data to initialize it with - depending on what type mUserToken will be, you can use appropriate Data initializer

init(bytes: UnsafeRawPointer, count: Int)
init(bytes: <Array<UInt8>>)
Related