1use crate::tape::{
2 FieldValue, Instruction, InstructionId, InstructionSet, InstructionSetTrait, InstructionTrait,
3 TapeMachine, Value,
4};
5use chrono::{DateTime, Utc};
6use std::{collections::HashMap, num::NonZeroU64};
7use tracing::Level;
8
9#[derive(Clone, Copy, Debug)]
10pub enum CacheInstruction<'a> {
11 Restart,
12 NewString(&'a str),
13 NewSpan {
14 parent: Option<NonZeroU64>,
15 span: NonZeroU64,
16 name: CacheString<'a>,
17 },
18 FinishedSpan,
19 NewRecord(NonZeroU64),
20 FinishedRecord,
21 StartEvent {
22 time: DateTime<Utc>,
23 span: Option<NonZeroU64>,
24 target: CacheString<'a>,
25 priority: Level,
26 },
27 FinishedEvent,
28 AddValue(FieldValue<'a, CacheString<'a>>),
29 DeleteSpan(NonZeroU64),
30}
31impl InstructionTrait for CacheInstruction<'_> {
32 fn id(self) -> InstructionId {
33 match self {
34 CacheInstruction::Restart => InstructionId::Restart,
35 CacheInstruction::NewString(..) => InstructionId::NewString,
36 CacheInstruction::NewSpan { .. } => InstructionId::NewSpan,
37 CacheInstruction::FinishedSpan => InstructionId::FinishedSpan,
38 CacheInstruction::NewRecord(..) => InstructionId::NewRecord,
39 CacheInstruction::FinishedRecord => InstructionId::FinishedRecord,
40 CacheInstruction::StartEvent { .. } => InstructionId::StartEvent,
41 CacheInstruction::FinishedEvent => InstructionId::FinishedEvent,
42 CacheInstruction::AddValue(..) => InstructionId::AddValue,
43 CacheInstruction::DeleteSpan(..) => InstructionId::DeleteSpan,
44 }
45 }
46}
47
48pub struct CacheInstructionSet;
49impl InstructionSetTrait for CacheInstructionSet {
50 type Instruction<'a> = CacheInstruction<'a>;
51}
52
53#[derive(Clone, Copy, Debug)]
54pub enum CacheString<'a> {
55 Present(&'a str),
56 Cached(u64),
57}
58
59pub struct StringCache<T> {
60 forward: T,
61 strings: HashMap<String, u64>,
62}
63impl<T> StringCache<T>
64where
65 T: TapeMachine<CacheInstructionSet>,
66{
67 pub fn new(forward: T) -> Self {
68 Self {
69 forward,
70 strings: Default::default(),
71 }
72 }
73
74 fn cache_value<'a>(&mut self, value: Value<'a, &'a str>) -> Value<'a, CacheString<'a>> {
75 match value {
76 Value::Debug(string) => Value::Debug(self.cache_string(string)),
77 Value::String(string) => Value::String(self.cache_string(string)),
78 Value::Float(value) => Value::Float(value),
79 Value::Integer(value) => Value::Integer(value),
80 Value::Unsigned(value) => Value::Unsigned(value),
81 Value::Bool(value) => Value::Bool(value),
82 Value::ByteArray(value) => Value::ByteArray(value),
83 }
84 }
85
86 fn cache_string<'a>(&mut self, string: &'a str) -> CacheString<'a> {
87 if let Some(id) = self.strings.get(string) {
88 return CacheString::Cached(*id);
89 }
90
91 let id = self.strings.len() as u64;
92 let present = !matches!(
93 (id, string.len()),
94 (0..=0xffff, 4..=255)
95 | (0x1_0000..=0xff_ffff, 5..=255)
96 | (0x100_0000..=0xff_ffff_ffff, 7..=255)
97 | (_, 11..)
98 );
99
100 if present {
101 CacheString::Present(string)
102 } else {
103 self.forward.handle(CacheInstruction::NewString(string));
104 self.strings.insert(string.to_owned(), id);
105 CacheString::Cached(id)
106 }
107 }
108}
109impl<T> TapeMachine<InstructionSet> for StringCache<T>
110where
111 T: TapeMachine<CacheInstructionSet>,
112{
113 fn needs_restart(&mut self) -> bool {
114 self.forward.needs_restart()
115 }
116
117 fn handle(&mut self, instruction: Instruction) {
118 match instruction {
119 Instruction::Restart => {
120 self.strings.clear();
121 self.forward.handle(CacheInstruction::Restart);
122 }
123 Instruction::NewSpan { parent, span, name } => {
124 let name = self.cache_string(name);
125 self.forward
126 .handle(CacheInstruction::NewSpan { parent, span, name });
127 }
128 Instruction::FinishedSpan => {
129 self.forward.handle(CacheInstruction::FinishedSpan);
130 }
131 Instruction::NewRecord(span) => {
132 self.forward.handle(CacheInstruction::NewRecord(span));
133 }
134 Instruction::FinishedRecord => {
135 self.forward.handle(CacheInstruction::FinishedRecord);
136 }
137 Instruction::StartEvent {
138 time,
139 span,
140 target,
141 priority,
142 } => {
143 let target = self.cache_string(target);
144 self.forward.handle(CacheInstruction::StartEvent {
145 time,
146 span,
147 target,
148 priority,
149 });
150 }
151 Instruction::FinishedEvent => {
152 self.forward.handle(CacheInstruction::FinishedEvent);
153 }
154 Instruction::AddValue(FieldValue { name, value }) => {
155 let name = self.cache_string(name);
156 let value = self.cache_value(value);
157 self.forward
158 .handle(CacheInstruction::AddValue(FieldValue { name, value }));
159 }
160 Instruction::DeleteSpan(span) => {
161 self.forward.handle(CacheInstruction::DeleteSpan(span));
162 }
163 }
164 }
165}
166
167pub struct StringUncache<T> {
168 forward: T,
169 strings: Vec<String>,
170}
171impl<T> StringUncache<T>
172where
173 T: TapeMachine<InstructionSet>,
174{
175 pub fn new(forward: T) -> Self {
176 Self {
177 forward,
178 strings: Default::default(),
179 }
180 }
181
182 fn uncache<'a>(strings: &'a [String], string: CacheString<'a>) -> &'a str {
183 match string {
184 CacheString::Present(str) => str,
185 CacheString::Cached(index) => strings[index as usize].as_str(),
186 }
187 }
188
189 fn uncache_value<'a>(
190 strings: &'a [String],
191 value: Value<'a, CacheString<'a>>,
192 ) -> Value<'a, &'a str> {
193 match value {
194 Value::Debug(string) => Value::Debug(Self::uncache(strings, string)),
195 Value::String(string) => Value::String(Self::uncache(strings, string)),
196 Value::Float(value) => Value::Float(value),
197 Value::Integer(value) => Value::Integer(value),
198 Value::Unsigned(value) => Value::Unsigned(value),
199 Value::Bool(value) => Value::Bool(value),
200 Value::ByteArray(items) => Value::ByteArray(items),
201 }
202 }
203}
204impl<T> TapeMachine<CacheInstructionSet> for StringUncache<T>
205where
206 T: TapeMachine<InstructionSet>,
207{
208 fn needs_restart(&mut self) -> bool {
209 self.forward.needs_restart()
210 }
211
212 fn handle(&mut self, instruction: CacheInstruction) {
213 match instruction {
214 CacheInstruction::Restart => {
215 self.forward.handle(Instruction::Restart);
216 }
217 CacheInstruction::NewString(str) => {
218 self.strings.push(str.to_owned());
219 }
220 CacheInstruction::NewSpan { parent, span, name } => {
221 let name = Self::uncache(&self.strings, name);
222 self.forward
223 .handle(Instruction::NewSpan { parent, span, name });
224 }
225 CacheInstruction::FinishedSpan => {
226 self.forward.handle(Instruction::FinishedSpan);
227 }
228 CacheInstruction::NewRecord(span) => {
229 self.forward.handle(Instruction::NewRecord(span));
230 }
231 CacheInstruction::FinishedRecord => {
232 self.forward.handle(Instruction::FinishedRecord);
233 }
234 CacheInstruction::StartEvent {
235 time,
236 span,
237 target,
238 priority,
239 } => {
240 let target = Self::uncache(&self.strings, target);
241
242 self.forward.handle(Instruction::StartEvent {
243 time,
244 span,
245 target,
246 priority,
247 });
248 }
249 CacheInstruction::FinishedEvent => {
250 self.forward.handle(Instruction::FinishedEvent);
251 }
252 CacheInstruction::AddValue(FieldValue { name, value }) => {
253 let name = Self::uncache(&self.strings, name);
254 let value = Self::uncache_value(&self.strings, value);
255 self.forward
256 .handle(Instruction::AddValue(FieldValue { name, value }));
257 }
258 CacheInstruction::DeleteSpan(span) => {
259 self.forward.handle(Instruction::DeleteSpan(span));
260 }
261 }
262 }
263}