| // Copyright 2025 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package trace |
| |
| import ( |
| "fmt" |
| "internal/trace/tracev2" |
| ) |
| |
| // timestamp is an unprocessed timestamp. |
| type timestamp uint64 |
| |
| type batch struct { |
| time timestamp |
| gen uint64 |
| data []byte |
| } |
| |
| // readBatch copies b and parses the trace batch header inside. |
| // Returns the batch, bytes read, and an error. |
| func readBatch(b []byte) (batch, uint64, error) { |
| if len(b) == 0 { |
| return batch{}, 0, fmt.Errorf("batch is empty") |
| } |
| data := make([]byte, len(b)) |
| copy(data, b) |
| |
| // Read batch header byte. |
| if typ := tracev2.EventType(b[0]); typ == tracev2.EvEndOfGeneration { |
| if len(b) != 1 { |
| return batch{}, 1, fmt.Errorf("unexpected end of generation in batch of size >1") |
| } |
| return batch{data: data}, 1, nil |
| } |
| if typ := tracev2.EventType(b[0]); typ != tracev2.EvEventBatch && typ != tracev2.EvExperimentalBatch { |
| return batch{}, 1, fmt.Errorf("expected batch event, got event %d", typ) |
| } |
| total := 1 |
| b = b[1:] |
| |
| // Read the generation |
| gen, n, err := readUvarint(b) |
| if err != nil { |
| return batch{}, uint64(total + n), fmt.Errorf("error reading batch gen: %w", err) |
| } |
| total += n |
| b = b[n:] |
| |
| // Read the M (discard it). |
| _, n, err = readUvarint(b) |
| if err != nil { |
| return batch{}, uint64(total + n), fmt.Errorf("error reading batch M ID: %w", err) |
| } |
| total += n |
| b = b[n:] |
| |
| // Read the timestamp. |
| ts, n, err := readUvarint(b) |
| if err != nil { |
| return batch{}, uint64(total + n), fmt.Errorf("error reading batch timestamp: %w", err) |
| } |
| total += n |
| b = b[n:] |
| |
| // Read the size of the batch to follow. |
| size, n, err := readUvarint(b) |
| if err != nil { |
| return batch{}, uint64(total + n), fmt.Errorf("error reading batch size: %w", err) |
| } |
| if size > tracev2.MaxBatchSize { |
| return batch{}, uint64(total + n), fmt.Errorf("invalid batch size %d, maximum is %d", size, tracev2.MaxBatchSize) |
| } |
| total += n |
| total += int(size) |
| if total != len(data) { |
| return batch{}, uint64(total), fmt.Errorf("expected complete batch") |
| } |
| data = data[:total] |
| |
| // Return the batch. |
| return batch{ |
| gen: gen, |
| time: timestamp(ts), |
| data: data, |
| }, uint64(total), nil |
| } |