@@ -219,6 +219,32 @@ fun commitContractDataAndActions(): void
219219 Auto packing structures to/from cells.
220220*/
221221
222+ /// PackOptions allows you to control behavior of `obj.toCell()` and similar functions.
223+ struct PackOptions {
224+ /// when a struct has a field of type `bits128` and similar (it's a slice under the hood),
225+ /// by default, compiler inserts runtime checks (get bits/refs count + compare with 128 + compare with 0);
226+ /// these checks ensure that serialized binary data will be correct, but they cost gas;
227+ /// however, if you guarantee that a slice is valid (for example, it comes from trusted sources),
228+ /// set this option to true to disable runtime checks;
229+ /// note: `int32` and other are always validated for overflow without any extra gas,
230+ /// so this flag controls only rarely used `bytesN` / `bitsN` types
231+ skipBitsNFieldsValidation: bool = false,
232+ }
233+
234+ /// UnpackOptions allows you to control behavior of `MyStruct.fromCell(c)` and similar functions.
235+ struct UnpackOptions {
236+ // after finished reading all fields from a cell/slice, call [slice.assertEnd] to ensure no remaining data left;
237+ // it's the default behavior, it ensures that you've fully described data you're reading with a struct;
238+ // example: `struct Point { x: int8; y: int8 }`, input "0102" is ok, "0102FF" will throw excno 9;
239+ // note: setting this to false does not decrease gas (DROP from a stack and ENDS cost the same);
240+ // note: this option controls [T.fromCell] and [T.fromSlice], but is ignored by [slice.loadAny]
241+ assertEndAfterReading: bool = true,
242+
243+ /// this excNo is thrown if a prefix doesn't match, e.g. for `struct (0x01) A` given input "88...";
244+ /// similarly, for a union type, this is thrown when none of the opcodes match
245+ throwIfOpcodeDoesNotMatch: int = 63,
246+ }
247+
222248/// Convert anything to a cell (most likely, you'll call it for structures).
223249/// Example:
224250/// ```
@@ -228,7 +254,7 @@ fun commitContractDataAndActions(): void
228254/// Internally, a builder is created, all fields are serialized one by one, and a builder is flushed
229255/// (beginCell() + serialize fields + endCell()).
230256@pure
231- fun T.toCell(self): Cell<T>
257+ fun T.toCell(self, options: PackOptions = {} ): Cell<T>
232258 builtin;
233259
234260/// Parse anything from a cell (most likely, you'll call it for structures).
@@ -239,7 +265,7 @@ fun T.toCell(self): Cell<T>
239265/// Internally, a cell is unpacked to a slice, and that slice is parsed
240266/// (packedCell.beginParse() + read from slice).
241267@pure
242- fun T.fromCell(packedCell: cell): T
268+ fun T.fromCell(packedCell: cell, options: UnpackOptions = {} ): T
243269 builtin;
244270
245271/// Parse anything from a slice (most likely, you'll call it for structures).
@@ -252,7 +278,7 @@ fun T.fromCell(packedCell: cell): T
252278/// Note, that a passed slice is NOT mutated, its internal pointer is NOT shifted.
253279/// If you need to mutate it, like `cs.loadInt()`, consider calling `cs.loadAny<CounterIncrement>()`.
254280@pure
255- fun T.fromSlice(rawSlice: slice): T
281+ fun T.fromSlice(rawSlice: slice, options: UnpackOptions = {} ): T
256282 builtin;
257283
258284/// Parse anything from a slice, shifting its internal pointer.
@@ -262,8 +288,10 @@ fun T.fromSlice(rawSlice: slice): T
262288/// var st: MyStorage = cs.loadAny(); // or cs.loadAny<MyStorage>()
263289/// ```
264290/// Similar to `MyStorage.fromSlice(cs)`, but called as a slice method and mutates the slice.
291+ /// Note: [options.assertEndAfterReading] is ignored by this function, because it's actually intended
292+ /// to read data from the middle.
265293@pure
266- fun slice.loadAny<T>(mutate self): T
294+ fun slice.loadAny<T>(mutate self, options: UnpackOptions = {} ): T
267295 builtin;
268296
269297/// Skip anything in a slice, shifting its internal pointer.
@@ -274,7 +302,7 @@ fun slice.loadAny<T>(mutate self): T
274302/// cs.skipAny<TwoInts>(); // skips 64 bits
275303/// ```
276304@pure
277- fun slice.skipAny<T>(mutate self): self
305+ fun slice.skipAny<T>(mutate self, options: UnpackOptions = {} ): self
278306 builtin;
279307
280308/// Store anything to a builder.
@@ -284,7 +312,19 @@ fun slice.skipAny<T>(mutate self): self
284312/// var b = beginCell().storeUint(32).storeAny(msgBody).endCell();
285313/// ```
286314@pure
287- fun builder.storeAny<T>(mutate self, v: T): self
315+ fun builder.storeAny<T>(mutate self, v: T, options: PackOptions = {}): self
316+ builtin;
317+
318+ /// Returns serialization prefix of a struct. Works at compile-time.
319+ /// Example: for `struct (0xF0) AssetRegular { ... }` will return `240`.
320+ @pure
321+ fun T.getDeclaredPackPrefix(): int
322+ builtin;
323+
324+ /// Returns serialization prefix length of a struct. Works at compile-time.
325+ /// Example: for `struct (0xF0) AssetRegular { ... }` will return `16`.
326+ @pure
327+ fun T.getDeclaredPackPrefixLen(): int
288328 builtin;
289329
290330/// Cell<T> represents a typed cell reference (as opposed to untyped `cell`).
@@ -316,7 +356,7 @@ struct Cell<T> {
316356/// var extra = st.extra.load(); // it's ExtraData, unpacked from loaded ref
317357/// ```
318358@pure
319- fun Cell<T>.load(self): T
359+ fun Cell<T>.load(self, options: UnpackOptions = {} ): T
320360 builtin;
321361
322362/// Converts a typed cell into a slice.
0 commit comments