4

With NSMutableData I could create an array of Int's or Float's and store those to disk.

protocol BinaryConvertible
{
    init()
}

extension Int : BinaryConvertible {}


struct Storage<T: BinaryConvertible>
{
let data = NSMutableData()

func append(value: T)
{
    var input = value
    data.append(&input, length: sizeof(T))
}

func extract(index: Int) -> T
{
    var output = T()
    let range = NSRange(location: index * sizeof(T), length: sizeof(T))
    data.getBytes(&output, range: range)
    return output
}
}

Swift 3 has a new Data type which uses NSData under the hood. Like String and NSString. I can't figure out how to add e.g. a Double using the new methods.

The append function now expects a UnsafePointer<UInt8>, but how do you create this from a Double or any random struct for that matter?

user965972
  • 2,489
  • 2
  • 23
  • 39
  • 1
    Possible duplicate of [Cast a Swift struct to UnsafeMutablePointer](http://stackoverflow.com/questions/29556610/cast-a-swift-struct-to-unsafemutablepointervoid) – Alexander Jun 20 '16 at 12:23
  • I don't think so. In swift 3 Data expects UnsafePointer. Note the UInt8 type. Everything I have found so far deals with UnsafePointer pointers. I need to get the bytes so I can write them to disk, and then later on read those bytes back. – user965972 Jun 20 '16 at 13:23
  • I don't see it. How can I convert a double to a series of bytes Uint8 that I can append to Data (not NSData or NSMutableData, but the new Swift 3 Data struct)? And then do the reverse: read the bytes and convert them to back to a Double. – user965972 Jun 20 '16 at 14:05

2 Answers2

11

Working with pointers is one of my least favorite thing to do in Swift, but it also offer a good learning experience. This works for me:

struct Storage<T: BinaryConvertible>
{
    var data = Data()

    mutating func append(value: T)
    {
        var input = value
        let buffer = UnsafeBufferPointer(start: &input, count: 1)
        self.data.append(buffer)
    }

    func extract(index: Int) -> T
    {
        let startIndex = index * sizeof(T)
        let endIndex = startIndex + sizeof(T)

        var output = T()
        let buffer = UnsafeMutableBufferPointer(start: &output, count: 1)
        let _ = self.data.copyBytes(to: buffer, from: startIndex..<endIndex)

        return output
    }
}

var s = Storage<Double>()
s.append(value: M_PI)
s.append(value: 42)
s.append(value: 100)

print(s.extract(index: 0))
print(s.extract(index: 1))
print(s.extract(index: 2))
Code Different
  • 90,614
  • 16
  • 144
  • 163
  • Hi, I can't find BinaryConvertible in the documentation, is that a protocol / class you made up? What would be the implementation in this case? Thanks – Paul Ollivier Feb 14 '21 at 11:13
4

I like to use + or +=

public protocol DataConvertible {
    static func + (lhs: Data, rhs: Self) -> Data
    static func += (lhs: inout Data, rhs: Self)
}

extension DataConvertible {
    public static func + (lhs: Data, rhs: Self) -> Data {
        var value = rhs
        let data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
        return lhs + data
    }

    public static func += (lhs: inout Data, rhs: Self) {
        lhs = lhs + rhs
    }
}

extension UInt8 : DataConvertible { }
extension UInt16 : DataConvertible { }
extension UInt32 : DataConvertible { }

extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }

extension String : DataConvertible {
    public static func + (lhs: Data, rhs: String) -> Data {
        guard let data = rhs.data(using: .utf8) else { return lhs}
        return lhs + data
    }
}

extension Data : DataConvertible {
    public static func + (lhs: Data, rhs: Data) -> Data {
        var data = Data()
        data.append(lhs)
        data.append(rhs)

        return data
    }
}

sample

var data = Data()
data += 1
data += 1.0
data += UInt8(1)
data += "1"
yycking
  • 1,017
  • 1
  • 9
  • 14