-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbinary.js
157 lines (130 loc) · 4.39 KB
/
binary.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
'use strict'
const { deserializeBinary, fnv1a } = require('@libp2p/observer-proto')
const getEmptyMessages = () => ({
states: [],
events: [],
runtime: null,
responses: [],
notices: [],
})
function getMessageChecksum(buffer) {
return fnv1a(buffer)
}
function parseBuffer(buf, deserialize = deserializeBinary) {
// Expects a binary file with this structure:
// - 4-byte version number
// - The following is repeated
// - 4-byte checksum of following message
// - 4-byte length of following message
// - message (typically state)
const byteLength = Buffer.byteLength(buf)
let bytesParsed = 0
const messages = getEmptyMessages()
const versionNumberLength = 4
const messageChecksumLength = 4
const messageSizeLength = 4
// Skip version number
bytesParsed += versionNumberLength
// TODO - add async variant
while (bytesParsed < byteLength) {
const messageChecksum = buf.readUIntLE(bytesParsed, messageChecksumLength)
bytesParsed += messageChecksumLength
const messageSize = buf.readUIntLE(bytesParsed, messageSizeLength)
const messageStart = bytesParsed + messageSizeLength
const messageEnd = messageStart + messageSize
const messageBin = buf.slice(messageStart, messageEnd)
const validChecksum = getMessageChecksum(messageBin) === messageChecksum
// TODO: bubble an error message for an invalid checksum
if (validChecksum) {
addMessageContent(messageBin, messages, deserialize)
} else {
console.error(
`Skipped bytes from ${messageStart} to ${messageEnd} due to checksum mismatch`
)
}
bytesParsed = messageEnd
}
return messages
}
function addMessage(message, messages) {
const runtimeContent = message.getRuntime()
const stateContent = message.getState()
const eventContent = message.getEvent()
const responseContent = message.getResponse()
const noticeContent = message.getNotice()
if (stateContent) {
messages.states.push(stateContent)
}
if (eventContent) {
messages.events.push(eventContent)
}
if (runtimeContent) {
// Only one runtime at a time
messages.runtime = runtimeContent
}
if (responseContent) {
messages.responses.push(responseContent)
}
if (noticeContent) {
messages.notices.push(noticeContent)
}
}
function addMessageContent(messageBin, messages, deserialize) {
const message = deserialize(messageBin)
addMessage(message, messages)
}
function parseArrayBuffer(arrayBuf, deserialize) {
return parseBuffer(Buffer.from(arrayBuf), deserialize)
}
function parseBase64(dataString, deserialize) {
return parseBuffer(Buffer.from(dataString, 'base64'), deserialize)
}
function parseBufferList(bufferList, deserialize = deserializeBinary) {
const messageChecksumLength = 4
const messageSizeLength = 4
const messages = getEmptyMessages()
while (bufferList.length > messageChecksumLength + messageSizeLength) {
// check for complete message in the buffer
const messageChecksum = bufferList.readUIntLE(0, messageChecksumLength)
const messageSize = messageSizeLength
? bufferList.readUIntLE(messageChecksumLength, messageSizeLength)
: bufferList.length
const minimalBufferLength =
messageChecksumLength + messageSizeLength + messageSize
if (bufferList.length < minimalBufferLength) break
// extract and verify message
const messageBin = bufferList.slice(
messageChecksumLength + messageSizeLength,
minimalBufferLength
)
// Smooth over environment consistencies between browser and node/tests
const messageBuf =
messageBin instanceof Buffer ? messageBin : Buffer.from(messageBin)
const calcChecksum = messageChecksumLength
? getMessageChecksum(messageBuf)
: messageChecksum
const valid = messageChecksum === calcChecksum
if (valid) {
const message = deserialize(messageBuf)
addMessage(message, messages)
} else {
console.error('Invalid data in buffer')
}
bufferList.consume(minimalBufferLength)
}
return messages
}
function parseImport(rawData, deserialize) {
if (rawData instanceof Buffer) return parseBuffer(rawData, deserialize)
if (rawData instanceof ArrayBuffer)
return parseArrayBuffer(rawData, deserialize)
if (rawData instanceof String) return parseBase64(rawData, deserialize)
return parseBufferList(rawData, deserialize)
}
module.exports = {
parseArrayBuffer,
parseBase64,
parseBuffer,
parseBufferList,
parseImport,
}