TraceMaker
C++ API of the engine
Loading...
Searching...
No Matches
replay_log.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2#pragma once
3// Replay log (design doc 09 ยง2, decision D45): the viewer protocol's JSON messages, one per line, each with a
4// leading "t" (seconds since the recording started). Written plain (`.jsonl`) or as a sequence of independent
5// zstd frames (`.zst`, about 10-20x smaller). Each frame holds whole lines and the board snapshot (the keyframe)
6// is a frame of its own, so a run that is killed loses at most the open frame, and any standard tool reads the
7// log (`zstd -dc run.jsonl.zst | jq`).
8#include <cstdint>
9#include <cstdio>
10#include <string>
11#include <string_view>
12
13// zstd contexts (zstd.h stays out of this header).
14struct ZSTD_CCtx_s;
15struct ZSTD_DCtx_s;
16
17namespace tmk::server {
18
20 public:
21 // Uncompressed bytes per zstd frame before it is closed: big enough for full compression ratio, small
22 // enough that a crash loses little.
23 static constexpr std::size_t kFrameBytes = 1u << 20;
24
25 // Compresses when `path` ends in ".zst". Throws std::runtime_error if the file cannot be created.
26 explicit ReplayWriter(const std::string& path, int level = 6);
28 ReplayWriter(const ReplayWriter&) = delete;
30
31 static bool compressed_path(std::string_view path);
32 bool compressed() const { return cctx_ != nullptr; }
33 void write_line(std::string_view line); // appends '\n'
34 void end_frame(); // closes the current zstd frame (no-op when plain or empty)
35 void close(); // ends the last frame and closes the file; called by the destructor
36 std::uint64_t bytes_in() const { return in_; }
37
38 private:
39 void write_out(const void* p, std::size_t n);
40 void compress(std::string_view data, bool end);
41 std::FILE* f_ = nullptr;
42 ::ZSTD_CCtx_s* cctx_ = nullptr;
43 std::string out_buf_;
44 std::size_t frame_in_ = 0; // uncompressed bytes in the open frame
45 std::uint64_t in_ = 0;
46};
47
49 public:
50 // Detects zstd by its magic number, so a renamed file still reads. Throws if the file cannot be opened.
51 explicit ReplayReader(const std::string& path);
53 ReplayReader(const ReplayReader&) = delete;
55
56 bool compressed() const { return dctx_ != nullptr; }
57 // Next complete line (without '\n'). A truncated tail (incomplete frame or line) is ignored. Throws on
58 // corrupt compressed data.
59 bool next_line(std::string& line);
60
61 private:
62 bool fill(); // decompress or read more into buf_; false at end of input
63 std::FILE* f_ = nullptr;
64 ::ZSTD_DCtx_s* dctx_ = nullptr;
65 std::string in_buf_;
66 std::size_t in_pos_ = 0, in_len_ = 0;
67 std::string out_; // decompression output scratch
68 std::string buf_; // decoded text not yet returned
69 std::size_t pos_ = 0;
70 bool out_pending_ = false;
71 bool eof_ = false;
72};
73
74} // namespace tmk::server
ReplayReader(const ReplayReader &)=delete
bool next_line(std::string &line)
Next complete line (without '\n').
ReplayReader & operator=(const ReplayReader &)=delete
ReplayReader(const std::string &path)
Detects zstd by its magic number, so a renamed file still reads. Throws if the file cannot be opened.
static constexpr std::size_t kFrameBytes
Uncompressed bytes per zstd frame before it is closed: big enough for full compression ratio,...
void close()
ends the last frame and closes the file; called by the destructor
std::uint64_t bytes_in() const
ReplayWriter(const ReplayWriter &)=delete
void write_line(std::string_view line)
appends '\n'
ReplayWriter(const std::string &path, int level=6)
Compresses when path ends in ".zst". Throws std::runtime_error if the file cannot be created.
static bool compressed_path(std::string_view path)
void end_frame()
closes the current zstd frame (no-op when plain or empty)
ReplayWriter & operator=(const ReplayWriter &)=delete