Protobuf streaming (la serializzazione pigra) API
Abbiamo un'applicazione Android che utilizza Protocol Buffers per memorizzare i dati delle applicazioni. Il formato dei dati (approssimativamente) è una singola protobuf ("contenitore") che contiene un elenco di protobufs ("oggetti") come field ripetuto:
message Container { repeated Item item = 1; }
Quando vogliamo salvare una modifica a un elemento, dobbiamo ricreare il contenitore di protobuf, aggiungere tutti gli elementi ad esso, poi serializzarlo e scrivere in un file.
Il problema di questo approccio è che triplica la memory utilizzata durante il salvataggio perché i dati vanno prima da copiare dalla class model al constructor di protocollo e poi ad un arrays di byte quando la protobuf viene serializzata, prima di scrivere su un file stream.
Quello che vorremmo è un modo per creare il nostro contenitore di protobuf e serializzarlo pigmentosamente in un stream, quindi aggiungere semplicemente each elemento protobuf (creato dai nostri dati model) al contenitore che la serializza e lo scriverà nel stream, piuttosto che mantenere tutti i elementi in memory finché non abbiamo creato l'integer contenitore in memory.
C'è un modo per build una protobuf e serializzarlo pigramente a un stream?
Se non esiste un modo per farlo ufficialmente, ci sono delle biblioteche che possono aiutare? Qualcuno ha suggerimenti o idee come risolvere questo in altri modi? Formati o tecnologie di dati alternativi (ad es. JSON o XML che contengono protobufs) che lo rendano ansible?
3 Solutions collect form web for “Protobuf streaming (la serializzazione pigra) API”
Per la serializzazione:
il protobuf è un formato appendibile, con l'unione di singoli elementi e l' aggiunta di elementi ripetuti
Pertanto, per scrivere una sequenza come un stream pigro, tutto quello che devi fare è ripetutamente scrivere la stessa struttura con solo una voce nell'elenco: la serializzazione di una sequenza di 200 x "Container con 1 elemento" è 100% identica alla serializzazione di 1 x "Container con 200 articoli".
Così: basta farlo!
Per la deserializzazione:
Questo è tecnicamente molto facile da leggere come un stream – tutto ciò, tuttavia, scende a quale libreria si sta utilizzando. Ad esempio, la esporrò in protobuf-net (implementazione .NET / C #) come Serializer.DeserializeItems<T>
, che legge (completamente pigra / streaming) una sequenza di messaggi di tipo T
, in base all'assunto che siano in il module che descrivi nella domanda (quindi Serializer.DeserializeItems<Item>
sarebbe il modo di streaming che sostituisce Serializer.Deserialize<Container>
– l'object più esterno tipo non esiste in protobuf)
Se questo non è disponibile, ma è ansible accedere a un API di lettura raw, è necessario:
- leggere un varint per l'intestazione – questo sarà il valore 10 (0x0A), cioè "(1 << 3) | 2" rispettivamente per il numero di field (1) e il tipo di filo (2) – quindi questo potrebbe anche essere formulato: "leggere un singolo byte dal stream e verificare che il valore sia di 10"
- leggere una varint per la lunghezza del seguente elemento
- adesso:
- se l'API del lettore consente di limitare il numero massimo di byte da elaborare, utilizzare questa lunghezza per specificare la lunghezza che segue
- o avvolgere l'API di stream con un stream di lunghezza limitato, limitato a tale lunghezza
- o semplicemente leggere manualmente molti byte e build un stream in memory dal carico utile
- risciacquare, ripetere
Non vi è nulla di simile. Un protobuf è una struttura imballata. Per farlo in modo efficace avrebbe bisogno di tutti i dati. Dovrai aggiungere il "protocollo di streaming". Forse submit un messaggio prototipo a each elemento N.
Nella versione normale di java dei buffer di protocollo ci sono file delimitati in cui si scrivono Protocol-Buffers uno alla volta. Non sono sicuro se è nella versione Android
aLocation.writeDelimitedTo(out);
Come Marc lo ha indicato facilmente implementato; basta scrivere una lunghezza seguita dai byte serializzati. Nella versione normale (non android) di java di prortocol-buffer si può anche fare (si deve serializzare un arrays di byte o qualcosa di simile)
private CodedOutputStream codedStream = null; public void write(byte[] bytes) throws IOException { if (bytes != ConstClass.EMPTY_BYTE_ARRAY) { codedStream.writeRawVarint32(bytes.length); codedStream.writeRawBytes(bytes); codedStream.flush(); } }
e
private CodedInputStream coded; public byte[] read() throws IOException { if (coded == null) { throw new IOException("Reader has not been opened !!!"); } if (coded.isAtEnd()) { return null; } return coded.readBytes().toByteArray();
Qualcosa può essere ansible in altre versioni di Protocol-Buffers