Как добавить данные int32 в протокол protobuf JS bytebuffer

Здравствуйте, мастера JavaScript,

Я пытаюсь сгенерировать файл на основе protobuf, используя javascript, который имеет следующую структуру:

messageSize | байты пробаффа | messageSize | байты протобаффа и т. д.

Идея состоит в том, что я добавляю несколько сообщений protobuf в один файл, чтобы потом обработать его, считав размер сообщения (4 байта целое), а затем перестроить сообщение pb, прочитав соответствующие следующие байты, а затем расшифровать каждое сообщение с помощью protobuf.,

У меня уже есть кодирование / декодирование, работающее в Objective-C, но я изо всех сил пытаюсь сделать то же самое с javascript. Поскольку код говорит сам за себя, вот как это делается с Objc (используя модуль 'ProtocolBuffers', '~> 1.9.8') в каждой итерации:

//configure protobuff, then build.
DataOperationPB * dataOp = [dataOperationBuilder build];
//get its NSData representation
NSData * varBlob = [dataOp data]; //byte string

unsigned int size = (unsigned int)[dataOp serializedSize];
[variablesBlobContainer appendData:[NSMutableData dataWithBytes:&size length:sizeof(size)]];
[variablesBlobContainer appendData:varBlob];

//then we can easily write this to a file with: 
[variablesBlobContainer writeToFile:fileNameWithPath atomically:YES]

Так просто; Если я открою сгенерированный файл и скажу, что размер первого сообщения протобуфера равен 250, начальные данные в файле будут правильно видны:

Просмотр первых 4 байтов файла (смещение 0) в HEX:

FA 00 00 00

А так же INT (LITTLE ENDIAN):

250

Работает как положено. У меня также есть декодирование, работающее с python, если вам удобнее пользоваться этим языком (для краткости удалены утверждения):

 file = open(currentPbdFile, 'r')
                    msgSize = file.read(4)
                    msg_len = struct.unpack('<L',msgSize)[0]
                    while msg_len > 0:
                            bufferVar = file.read(msg_len)
                            dataOpList.append(DataOperation(bufferVar))
                            msgSize = file.read(4)

                            if(msgSize == ''):
                                    break
                            msg_len = struct.unpack('<L', msgSize)[0]

Теперь, когда я пытаюсь сделать то же самое с JavaScript, я борюсь с этим. Одна из моих попыток была (используя protobuf.js для кодирования сообщений):

var ProtoBuf = dcodeIO.ProtoBuf;
var builder = ProtoBuf.loadProtoFile("DataOperationPB.proto");

var ByteBuffer = dcodeIO.ByteBuffer;
var byteBuffer = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
var data = new DataOperationPB({
                //(omitted code for setting pb message values)
            });

Затем в цикле я создаю сообщение и добавляю данные:

byteBuffer.append(new ByteBuffer().writeInt32(data.calculate()));
byteBuffer.append(data.encode()); //the protobuff data itself

Позже я предоставлю данные в виде кнопки загрузки URL:

var data = new Blob([new DataView(byteBuffer.toArrayBuffer())], {type: 'application/octet-stream'});

Из углового:

this.url = ($window.URL || $window.webkitURL).createObjectURL(data);

Когда я открываю загруженный файл для сообщения protobuff длиной 29, первые четыре байта выглядят так:

28 01 38 04

Что совершенно неправильно.

Пройдя немного дальше, я заметил, что protobuf.js использует собственную реализацию ArrayBuffers (называемую ByteBuffer.js), которая, в свою очередь, использует обычные ArrayBuffers при запуске Javascript в браузере. Я, не будучи старшим в JS, может кто-нибудь указать направление для достижения вышеупомянутого? Заранее благодарю за любую помощь.

1 ответ

Отвечая на мой собственный вопрос... разработчик библиотеки помог мне разобраться.

Ловушка, в которую я попал, не замечала, что метод.toArrayBuffer() должен эффективно выполнять чтение, а при переключении с операций на запись / чтение необходимо выполнить flip(), вот здесь:

//the important, forgotten flip() - 'implicit' read operation:
new Blob([new DataView(byteBuffer.flip().toArrayBuffer())]

Кроме того, при непосредственном использовании.writeInt32() переворот не требуется, тогда как добавление вновь созданного byteBuffer требует:

for(...){
    byteBuffer.writeInt32(data.calculate());
    // -- or --
    byteBuffer.append(new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN).writeInt32(data.calculate()).flip());
}

Вот больше документации об этом. Надеюсь, это кому-нибудь поможет.

С наилучшими пожеланиями!

Другие вопросы по тегам