Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to append Protocol Buffers in Swift?

I have a protobuf v2 in Swift and I'm trying to append it to another protobuf. This is what I'm trying:

let attachment = getAttachment(id: 987) //From cloud database
var protosData = NSMutableData(data: attachment)

items.forEach { //Some struct array of values
  guard let proto = try? MyProtoBuf.Builder()
      .setEpochMillis($0.date.epochMilliseconds)
      .setValue($0.value)
      .build() else { return }

  protosData.appendData(proto.data())
}

saveAttachment(protosData) //Store to cloud

It seems I'm like I'm corrupting the data because I get this error when reading it back:

malloc: *** mach_vm_map(size=2749415424) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug

Perhaps it is my reading back the values is incorrect, here is what I'm doing to read the appended data from storage:

extension GeneratedMessageProtocol {

  static func getStreamData(data: NSData) -> [Self] {
    var messages = [Self]()
    do {
        let inStream = NSInputStream(data:data)
        inStream.open()
        defer { inStream.close() }
        while inStream.hasBytesAvailable {
            var sizeBuffer: [UInt8] = [0,0,0,0]
            inStream.read(&sizeBuffer, maxLength: sizeBuffer.count)
            let data = NSData(bytes: sizeBuffer, length: sizeBuffer.count)
            let messageSize = data.uint32.littleEndian
            var buffer = Array<UInt8>(count: Int(messageSize), repeatedValue: 0)
            inStream.read(&buffer, maxLength: Int(messageSize))
            let messageData = NSData(bytes: buffer, length:Int(messageSize))
            messages.append(try self.parseFromData(messageData))
        }
    }
    catch {

    }
    return messages
  }
}

extension NSData {

  var uint32: UInt32 {
    get {
        var number: UInt32 = 0
        self.getBytes(&number, length: sizeof(UInt32))
        return number
    }
  }
}

And here is my protobuf message:

syntax = "proto2";

message MyProtoBuf {
    optional uint64 epochMillis = 1;
    optional uint32 value = 2;
}

What is the correct way of appending data to an existing protobuf instead of having to parse the array items one by one, appending the protobuf, then converting the whole array back to bytes?

like image 442
TruMan1 Avatar asked Aug 08 '16 21:08

TruMan1


1 Answers

your reading part is ok. you are missing delimiters when chaining proto objects. first calculate and add delimiter to the stream and then the proto object. then do it for each proto object.

let attachment = getAttachment(id: 987)         //From cloud database
var arr: [UInt32] = [UInt32(attachment.length)] //Assuming attachment is NSData type
let delimiter = NSData(bytes: arr, length: arr.count * sizeof(UInt32))

let protosData = NSMutableData(data: delimiter)
protosData.appendData(attachment)

items.forEach {                                 //Some struct array of values
  guard let proto = try? MyProtoBuf.Builder()
      .setEpochMillis($0.date.epochMilliseconds)
      .setValue($0.value)
      .build() else { return }

  var array: [UInt32] = [UInt32(proto.data().length)]
  let delimit = NSData(bytes: array, length: arr.count * sizeof(UInt32))

  protosData.appendData(delimit)
  protosData.appendData(proto.data())
}

saveAttachment(protosData)                      //Store to cloud
like image 195
ha100 Avatar answered Nov 16 '22 08:11

ha100