Spicy
stream.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <hilti/ast/expressions/id.h>
6 #include <hilti/ast/operators/common.h>
7 #include <hilti/ast/types/bool.h>
8 #include <hilti/ast/types/bytes.h>
9 #include <hilti/ast/types/integer.h>
10 #include <hilti/ast/types/stream.h>
11 
12 namespace hilti {
13 namespace operator_ {
14 
15 // stream::Iterator
16 
17 STANDARD_OPERATOR_1(stream::iterator, Deref, type::UnsignedInteger(64), type::constant(type::stream::Iterator()),
18  "Returns the character the iterator is pointing to.");
19 STANDARD_OPERATOR_1(stream::iterator, IncrPostfix, type::stream::Iterator(), type::stream::Iterator(),
20  "Advances the iterator by one byte, returning the previous position.");
21 STANDARD_OPERATOR_1(stream::iterator, IncrPrefix, type::stream::Iterator(), type::stream::Iterator(),
22  "Advances the iterator by one byte, returning the new position.");
23 
24 STANDARD_OPERATOR_2(
25  stream::iterator, Equal, type::Bool(), type::constant(type::stream::Iterator()),
26  type::constant(type::stream::Iterator()),
27  "Compares the two positions. The result is undefined if they are not referring to the same stream value.");
28 STANDARD_OPERATOR_2(
29  stream::iterator, Unequal, type::Bool(), type::constant(type::stream::Iterator()),
30  type::constant(type::stream::Iterator()),
31  "Compares the two positions. The result is undefined if they are not referring to the same stream value.");
32 STANDARD_OPERATOR_2(
33  stream::iterator, Lower, type::Bool(), type::constant(type::stream::Iterator()),
34  type::constant(type::stream::Iterator()),
35  "Compares the two positions. The result is undefined if they are not referring to the same stream value.");
36 STANDARD_OPERATOR_2(
37  stream::iterator, LowerEqual, type::Bool(), type::constant(type::stream::Iterator()),
38  type::constant(type::stream::Iterator()),
39  "Compares the two positions. The result is undefined if they are not referring to the same stream value.");
40 STANDARD_OPERATOR_2(
41  stream::iterator, Greater, type::Bool(), type::constant(type::stream::Iterator()),
42  type::constant(type::stream::Iterator()),
43  "Compares the two positions. The result is undefined if they are not referring to the same stream value.");
44 STANDARD_OPERATOR_2(
45  stream::iterator, GreaterEqual, type::Bool(), type::constant(type::stream::Iterator()),
46  type::constant(type::stream::Iterator()),
47  "Compares the two positions. The result is undefined if they are not referring to the same stream value.");
48 STANDARD_OPERATOR_2(
49  stream::iterator, Difference, type::SignedInteger(64), type::constant(type::stream::Iterator()),
50  type::constant(type::stream::Iterator()),
51  "Returns the number of stream between the two iterators. The result will be negative if the second iterator points "
52  "to a location before the first. The result is undefined if the iterators do not refer to the same stream "
53  "instance.");
54 STANDARD_OPERATOR_2(stream::iterator, Sum, type::stream::Iterator(), type::constant(type::stream::Iterator()),
55  type::UnsignedInteger(64), "Advances the iterator by the given number of stream.")
56 STANDARD_OPERATOR_2(stream::iterator, SumAssign, type::stream::Iterator(), type::stream::Iterator(),
57  type::UnsignedInteger(64), "Advances the iterator by the given number of stream.")
58 
59 BEGIN_METHOD(stream::iterator, Offset)
60  auto signature() const {
61  return Signature{.self = type::constant(type::stream::Iterator()),
62  .result = type::UnsignedInteger(64),
63  .id = "offset",
64  .args = {},
65  .doc = R"(
66 Returns the offset of the byte that the iterator refers to relative to the
67 beginning of the underlying stream value.
68 )"};
69  }
70 END_METHOD
71 
72 BEGIN_METHOD(stream::iterator, IsFrozen)
73  auto signature() const {
74  return Signature{.self = type::constant(type::stream::Iterator()),
75  .result = type::Bool(),
76  .id = "is_frozen",
77  .args = {},
78  .doc = R"(
79 Returns whether the stream value that the iterator refers to has been frozen.
80 )"};
81  }
82 END_METHOD
83 
84 // stream::View
85 
86 STANDARD_OPERATOR_1(stream::view, Size, type::UnsignedInteger(64), type::constant(type::stream::View()),
87  "Returns the number of stream the view contains.");
88 STANDARD_OPERATOR_2x(stream::view, InBytes, In, type::Bool(), type::constant(type::stream::View()),
89  type::constant(type::Bytes()),
90  "Returns true if the right-hand-side view contains the left-hand-side bytes as a subsequence.");
91 STANDARD_OPERATOR_2x(stream::view, InView, In, type::Bool(), type::constant(type::Bytes()),
92  type::constant(type::stream::View()),
93  "Returns true if the right-hand-side bytes contains the left-hand-side view as a subsequence.");
94 STANDARD_OPERATOR_2x(stream::view, EqualView, Equal, type::Bool(), type::constant(type::stream::View()),
95  type::constant(type::stream::View()), "Compares the views lexicographically.");
96 STANDARD_OPERATOR_2x(stream::view, EqualBytes, Equal, type::Bool(), type::constant(type::stream::View()),
97  type::constant(type::Bytes()), "Compares a stream view and a bytes instance lexicographically.");
98 STANDARD_OPERATOR_2x(stream::view, UnequalView, Unequal, type::Bool(), type::constant(type::stream::View()),
99  type::constant(type::stream::View()), "Compares two views lexicographically.");
100 STANDARD_OPERATOR_2x(stream::view, UnequalBytes, Unequal, type::Bool(), type::constant(type::stream::View()),
101  type::constant(type::Bytes()), "Compares a stream view and a bytes instance lexicographically.");
102 
103 BEGIN_METHOD(stream::view, Offset)
104  auto signature() const {
105  return Signature{.self = type::constant(type::stream::View()),
106  .result = type::UnsignedInteger(64),
107  .id = "offset",
108  .args = {},
109  .doc = R"(
110 Returns the offset of the view's starting position within the associated stream value.
111 )"};
112  }
113 END_METHOD
114 
115 BEGIN_METHOD(stream::view, AdvanceBy)
116  auto signature() const {
117  return Signature{.self = type::constant(type::stream::View()),
118  .result = type::stream::View(),
119  .id = "advance",
120  .args = {{.id = "i", .type = type::stream::Iterator()}},
121  .doc = R"(
122 Advances the view's starting position to a given iterator *i*, returning the new
123 view. The iterator must be referring to the same stream values as the view, and
124 it must be equal or ahead of the view's starting position.
125 )"};
126  }
127 END_METHOD
128 
129 BEGIN_METHOD(stream::view, AdvanceToNextData)
130  auto signature() const {
131  return Signature{.self = type::constant(type::stream::View()),
132  .result = type::stream::View(),
133  .id = "advance_to_next_data",
134  .args = {},
135  .doc = R"(
136 Advances the view's starting position to the next non-gap position. This always
137 advances the input by at least one byte.
138 )"};
139  }
140 END_METHOD
141 
142 BEGIN_METHOD(stream::view, Limit)
143  auto signature() const {
144  return Signature{.self = type::constant(type::stream::View()),
145  .result = type::stream::View(),
146  .id = "limit",
147  .args = {{.id = "i", .type = type::UnsignedInteger(64)}},
148  .doc = R"(
149 Returns a new view that keeps the current start but cuts off the end *i*
150 characters from that beginning. The returned view will not be able to expand any
151 further.
152 )"};
153  }
154 END_METHOD
155 
156 BEGIN_METHOD(stream::view, AdvanceTo)
157  auto signature() const {
158  return Signature{.self = type::constant(type::stream::View()),
159  .result = type::stream::View(),
160  .id = "advance",
161  .args = {{.id = "i", .type = type::UnsignedInteger(64)}},
162  .doc = R"(
163 Advances the view's starting position by *i* stream, returning the new view.
164 )"};
165  }
166 END_METHOD
167 
168 BEGIN_METHOD(stream::view, Find)
169  auto signature() const {
170  return Signature{.self = type::constant(type::stream::View()),
171  .result = type::Tuple({type::Bool(), type::stream::Iterator()}),
172  .id = "find",
173  .args = {{.id = "needle", .type = type::constant(type::Bytes())}},
174  .doc = R"(
175 Searches *needle* inside the view's content. Returns a tuple of a boolean and an
176 iterator. If *needle* was found, the boolean will be true and the iterator will point
177 to its first occurrence. If *needle* was not found, the boolean will be false and
178 the iterator will point to the last position so that everything before that is
179 guaranteed to not contain even a partial match of *needle* (in other words: one can
180 trim until that position and then restart the search from there if more data
181 gets appended to the underlying stream value). Note that for a simple yes/no result,
182 you should use the ``in`` operator instead of this method, as it's more efficient.
183 )"};
184  }
185 END_METHOD
186 
187 BEGIN_METHOD(stream::view, At)
188  auto signature() const {
189  return Signature{.self = type::constant(type::stream::View()),
190  .result = type::stream::Iterator(),
191  .id = "at",
192  .args = {{.id = "i", .type = type::UnsignedInteger(64)}},
193  .doc = R"(
194 Returns an iterator representing the offset *i* inside the view.
195 )"};
196  }
197 END_METHOD
198 
199 BEGIN_METHOD(stream::view, StartsWith)
200  auto signature() const {
201  return Signature{.self = type::constant(type::stream::View()),
202  .result = type::Bool(),
203  .id = "starts_with",
204  .args = {{.id = "b", .type = type::constant(type::Bytes())}},
205  .doc = R"(
206 Returns true if the view starts with *b*.
207 )"};
208  }
209 END_METHOD
210 
211 BEGIN_METHOD(stream::view, SubIterators)
212  auto signature() const {
213  return Signature{.self = type::constant(type::stream::View()),
214  .result = type::stream::View(),
215  .id = "sub",
216  .args = {{.id = "begin", .type = type::stream::Iterator()},
217  {.id = "end", .type = type::stream::Iterator()}},
218  .doc = R"(
219 Returns a new view of the subsequence from *begin* up to (but not including)
220 *end*.
221 )"};
222  }
223 END_METHOD
224 
225 BEGIN_METHOD(stream::view, SubIterator)
226  auto signature() const {
227  return Signature{.self = type::constant(type::stream::View()),
228  .result = type::stream::View(),
229  .id = "sub",
230  .args = {{.id = "end", .type = type::stream::Iterator()}},
231  .doc = R"(
232 Returns a new view of the subsequence from the beginning of the stream up to
233 (but not including) *end*.
234 )"};
235  }
236 END_METHOD
237 
238 BEGIN_METHOD(stream::view, SubOffsets)
239  auto signature() const {
240  return Signature{.self = type::constant(type::stream::View()),
241  .result = type::stream::View(),
242  .id = "sub",
243  .args = {{.id = "begin", .type = type::UnsignedInteger(64)},
244  {.id = "end", .type = type::UnsignedInteger(64)}},
245  .doc = R"(
246 Returns a new view of the subsequence from offset *begin* to (but not including)
247 offset *end*. The offsets are relative to the beginning of the view.
248 )"};
249  }
250 END_METHOD
251 
252 // Stream
253 
254 STANDARD_OPERATOR_1(stream, Size, type::UnsignedInteger(64), type::constant(type::Stream()),
255  "Returns the number of stream the value contains.");
256 STANDARD_OPERATOR_2(stream, Unequal, type::Bool(), type::constant(type::Stream()), type::constant(type::Stream()),
257  "Compares two stream values lexicographically.");
258 STANDARD_OPERATOR_2x(stream, SumAssignView, SumAssign, type::Stream(), type::Stream(),
259  type::constant(type::stream::View()), "Concatenates another stream's view to the target stream.");
260 STANDARD_OPERATOR_2x(stream, SumAssignBytes, SumAssign, type::Stream(), type::Stream(), type::constant(type::Bytes()),
261  "Concatenates data to the stream.");
262 
263 BEGIN_METHOD(stream, Freeze)
264  auto signature() const {
265  return Signature{.self = type::Stream(), .result = type::void_, .id = "freeze", .args = {}, .doc = R"(
266 Freezes the stream value. Once frozen, one cannot append any more data to a
267 frozen stream value (unless it gets unfrozen first). If the value is
268 already frozen, the operation does not change anything.
269 )"};
270  }
271 END_METHOD
272 
273 BEGIN_METHOD(stream, Unfreeze)
274  auto signature() const {
275  return Signature{.self = type::Stream(), .result = type::void_, .id = "unfreeze", .args = {}, .doc = R"(
276 Unfreezes the stream value. A unfrozen stream value can be further modified. If
277 the value is already unfrozen (which is the default), the operation does not
278 change anything.
279 )"};
280  }
281 END_METHOD
282 
283 BEGIN_METHOD(stream, IsFrozen)
284  auto signature() const {
285  return Signature{.self = type::constant(type::Stream()),
286  .result = type::Bool(),
287  .id = "is_frozen",
288  .args = {},
289  .doc = R"(
290 Returns true if the stream value has been frozen.
291 )"};
292  }
293 END_METHOD
294 
295 BEGIN_METHOD(stream, At)
296  auto signature() const {
297  return Signature{.self = type::constant(type::Stream()),
298  .result = type::stream::Iterator(),
299  .id = "at",
300  .args = {{.id = "i", .type = type::UnsignedInteger(64)}},
301  .doc = R"(
302 Returns an iterator representing the offset *i* inside the stream value.
303 )"};
304  }
305 END_METHOD
306 
307 BEGIN_METHOD(stream, Trim)
308  auto signature() const {
309  return Signature{.self = type::Stream(),
310  .result = type::void_,
311  .id = "trim",
312  .args = {{.id = "i", .type = type::stream::Iterator()}},
313  .doc = R"(
314 Trims the stream value by removing all data from its beginning up to (but not
315 including) the position *i*. The iterator *i* will remain valid afterwards and
316 will still point to the same location, which will now be the beginning of the stream's
317 value. All existing iterators pointing to *i* or beyond will remain valid and keep
318 their offsets as well. The effect of this operation is undefined if *i* does not
319 actually refer to a location inside the stream value. Trimming is permitted
320 even on frozen values.
321 )"};
322  }
323 END_METHOD
324 
325 } // namespace operator_
326 } // namespace hilti