Read the next outer message from the buffer. The message is returned by value so it does not escape to the heap (its single caller, Start, consumes it immediately and never retains it).
()
| 206 | // value so it does not escape to the heap (its single caller, Start, consumes |
| 207 | // it immediately and never retains it). |
| 208 | func (p *Parser) readOuterMessage() (outerMessage, error) { |
| 209 | // Read a command header, which includes both the message type |
| 210 | // well as a flag to determine whether or not whether or not the |
| 211 | // message is compressed with snappy. |
| 212 | command, err := p.stream.readCommand() |
| 213 | if err != nil { |
| 214 | return outerMessage{}, err |
| 215 | } |
| 216 | |
| 217 | // Extract the type and compressed flag out of the command |
| 218 | msgType := int32(command & ^dota.EDemoCommands_DEM_IsCompressed) |
| 219 | msgCompressed := (command & dota.EDemoCommands_DEM_IsCompressed) == dota.EDemoCommands_DEM_IsCompressed |
| 220 | |
| 221 | // Read the tick that the message corresponds with. |
| 222 | tick, err := p.stream.readVarUint32() |
| 223 | if err != nil { |
| 224 | return outerMessage{}, err |
| 225 | } |
| 226 | |
| 227 | // This appears to actually be an int32, where a -1 means pre-game. |
| 228 | if tick == 4294967295 { |
| 229 | tick = 0 |
| 230 | } |
| 231 | |
| 232 | // Read the size and following buffer. |
| 233 | size, err := p.stream.readVarUint32() |
| 234 | if err != nil { |
| 235 | return outerMessage{}, err |
| 236 | } |
| 237 | |
| 238 | // Reject an implausibly large size before allocating, so a corrupt or |
| 239 | // truncated stream fails cleanly instead of attempting a huge allocation. |
| 240 | if size > maxOuterMessageSize { |
| 241 | return outerMessage{}, _errorf("outer message size %d exceeds maximum %d", size, maxOuterMessageSize) |
| 242 | } |
| 243 | |
| 244 | buf, err := p.stream.readBytes(size) |
| 245 | if err != nil { |
| 246 | return outerMessage{}, err |
| 247 | } |
| 248 | |
| 249 | // If the buffer is compressed, decompress it with snappy, reusing a |
| 250 | // parser-level scratch buffer across messages. snappy.Decode reuses the |
| 251 | // destination when it is large enough, amortizing the decompression |
| 252 | // allocation to roughly the largest compressed message seen. This is safe |
| 253 | // because the decoded buffer is consumed within the dispatch of this |
| 254 | // message and never retained across outer messages. |
| 255 | if msgCompressed { |
| 256 | var err error |
| 257 | if buf, err = snappy.Decode(p.snappyScratch[:cap(p.snappyScratch)], buf); err != nil { |
| 258 | return outerMessage{}, err |
| 259 | } |
| 260 | p.snappyScratch = buf |
| 261 | } |
| 262 | |
| 263 | // Return the message |
| 264 | return outerMessage{ |
| 265 | tick: tick, |
no test coverage detected