Skip to content
prcela edited this page May 24, 2016 · 8 revisions

Un/packing complex hierarchy with MessagePack serialization format.

This readme file is a small helper for using swift project a2/MessagePack.swift

For this concept let use a simple structure that is contained as array in parent structure.

struct Point {
   var id: Int64
   var x: Double
   var y: double

  init(id: Int64, x: Double, y: Double)
  {
    self.id = id
    self.x = x
    self.y = y
  }
}

struct Triangle {
  let id: Int64
  let points: [Point]

  init(id: Int64, points: [Point])
  {
    self.id = id
    self.points = points
  }

}

Define the packing behavior with following protocol:

protocol Packable
{
  init(value:MessagePackValue)
  func messagePackValue() -> MessagePackValue
}

Init function unpacks the native struct from a packed value, while the messagePackValue() packs the struct into packed value.

Extend the Point struct with Packable protocol:

struct Point: Packable {
  ...
  init(value: MessagePackValue)
  {
    var gen = value.arrayValue!.generate()
    
    id = gen.next()!.integerValue!
    x = gen.next()!.doubleValue!
    y = gen.next()!.doubleValue!
  }

  func messagePackValue() -> MessagePackValue
  {
    let value: MessagePackValue = [
        MessagePackValue(id),
        MessagePackValue(x),
        MessagePackValue(y)
    ]
    return value
  }

}

Extend the Triangle struct with Packable protocol:

struct Triangle: Packable {
  let id: Int64
  let points: [Point]

  ...

  init(value: MessagePackValue)
  {
    var gen = value.arrayValue!.generate()
    
    id = gen.next()!.integerValue!
    points = gen.next()!.arrayValue!.map { (value) -> Point in
      return Point(value: value)
      }      
  }

  func messagePackValue() -> MessagePackValue
  {
    let value: MessagePackValue = [
        MessagePackValue(id),
        MessagePackValue(points.map { (p) -> MessagePackValue in 
          return p.messagePackValue()
        })
    ]
    return value
  }
  
}

Now lets pack some triangle struct into data:

  let p0 = Point(id: 1, x: 2, y: 3)
  let p1 = Point(id: 2, x: 5, y: 6)
  let p2 = Point(id: 3, x: 5, y: 7)
  let t = Triangle(id: 7, points: [p0,p1,p2])

  let packed = pack(t.messagePackValue())
  let data = packed.withUnsafeBufferPointer({ buffer in
    return NSData(bytes: buffer.baseAddress, length: buffer.count)
  })

Now you have the data ready for writing to file, sending over the net, or similar.

Let us unpack the pack data into triangle

do {
    let bytes = data.bytes()
    let value = try unpack(bytes)
    let triangle = Triangle(value: value)
    print("y coord of last point in tringle: \(triangle.points.last!.y)")
} catch let e as NSError
{
    print(e.description)
}

For the code purity, the bytes converting function is isolated as the NSData extension:

extension NSData
{
  func bytes() -> [Byte]
  {
    let count = length / sizeof(Byte)
    
    // create array of appropriate length:
    var byteData = [Byte](count: count, repeatedValue: 0)
    
    // copy bytes into byte array
    getBytes(&byteData, length:count * sizeof(Byte))
    
    return byteData
  }
}
Clone this wiki locally