in play.go [110:176]
func decode(kind string, output []byte) ([]event, error) {
var (
magic = []byte{0, 0, 'P', 'B'}
headerLen = 8 + 4
last = epoch
events []event
)
add := func(t time.Time, b []byte) {
var prev *event
if len(events) > 0 {
prev = &events[len(events)-1]
}
if prev != nil && t.Equal(prev.time) {
// Merge this event with previous event, to avoid
// sending a lot of events for a big output with no
// significant timing information.
prev.msg = append(prev.msg, b...)
} else {
e := event{msg: b, kind: kind, time: t}
events = append(events, e)
}
last = t
}
for i := 0; i < len(output); {
if !bytes.HasPrefix(output[i:], magic) {
// Not a header; find next header.
j := bytes.Index(output[i:], magic)
if j < 0 {
// No more headers; bail.
add(last, output[i:])
break
}
add(last, output[i:i+j])
i += j
}
i += len(magic)
// Decode header.
if len(output)-i < headerLen {
return nil, errors.New("short header")
}
header := output[i : i+headerLen]
nanos := int64(binary.BigEndian.Uint64(header[0:]))
t := time.Unix(0, nanos)
if t.Before(last) {
// Force timestamps to be monotonic. (This could
// be an encoding error, which we ignore now but will
// will likely be picked up when decoding the length.)
t = last
}
n := int(binary.BigEndian.Uint32(header[8:]))
if n < 0 {
return nil, fmt.Errorf("bad length: %v", n)
}
i += headerLen
// Slurp output.
// Truncated output is OK (probably caused by sandbox limits).
end := i + n
if end > len(output) {
end = len(output)
}
add(t, output[i:end])
i += n
}
return events, nil
}