1use std::collections::HashMap;
2use std::ops::Add;
3use std::sync::Arc;
4
5use bitstream_io::{BigEndian, BitWrite, BitWriter};
6use num_bigint::{BigInt, BigUint, Sign};
7use num_traits::{One, Zero};
8
9use crate::cell::dict::{DictBuilder, ValWriter};
10use crate::cell::error::{MapTonCellError, TonCellError};
11use crate::cell::{ArcCell, Cell, CellParser};
12use crate::TonAddress;
13
14pub(crate) const MAX_CELL_BITS: usize = 1023;
15pub(crate) const MAX_CELL_REFERENCES: usize = 4;
16pub(crate) const MAX_LEVEL_MASK: u32 = 3;
17
18pub struct CellBuilder {
19 bit_writer: BitWriter<Vec<u8>, BigEndian>,
20 bits_to_write: usize,
21 references: Vec<ArcCell>,
22 is_cell_exotic: bool,
23}
24
25#[derive(Clone, Debug, PartialEq, Copy)]
26pub enum EitherCellLayout {
27 Native,
28 ToRef,
29 ToCell,
30}
31
32impl CellBuilder {
33 pub fn new() -> CellBuilder {
34 let bit_writer = BitWriter::endian(Vec::new(), BigEndian);
35 CellBuilder {
36 bit_writer,
37 bits_to_write: 0,
38 references: Vec::new(),
39 is_cell_exotic: false,
40 }
41 }
42
43 pub fn set_cell_is_exotic(&mut self, val: bool) {
44 self.is_cell_exotic = val;
45 }
46
47 pub fn store_bit(&mut self, val: bool) -> Result<&mut Self, TonCellError> {
48 self.bit_writer.write_bit(val).map_cell_builder_error()?;
49 self.bits_to_write += 1;
50 Ok(self)
51 }
52
53 pub fn store_u8(&mut self, bit_len: usize, val: u8) -> Result<&mut Self, TonCellError> {
54 self.bit_writer
55 .write(bit_len as u32, val)
56 .map_cell_builder_error()?;
57 self.bits_to_write += bit_len;
58 Ok(self)
59 }
60
61 pub fn store_i8(&mut self, bit_len: usize, val: i8) -> Result<&mut Self, TonCellError> {
62 self.bit_writer
63 .write(bit_len as u32, val)
64 .map_cell_builder_error()?;
65 self.bits_to_write += bit_len;
66 Ok(self)
67 }
68
69 pub fn store_u32(&mut self, bit_len: usize, val: u32) -> Result<&mut Self, TonCellError> {
70 self.bit_writer
71 .write(bit_len as u32, val)
72 .map_cell_builder_error()?;
73 self.bits_to_write += bit_len;
74 Ok(self)
75 }
76
77 pub fn store_i32(&mut self, bit_len: usize, val: i32) -> Result<&mut Self, TonCellError> {
78 self.bit_writer
79 .write(bit_len as u32, val)
80 .map_cell_builder_error()?;
81 self.bits_to_write += bit_len;
82 Ok(self)
83 }
84
85 pub fn store_u64(&mut self, bit_len: usize, val: u64) -> Result<&mut Self, TonCellError> {
86 self.bit_writer
87 .write(bit_len as u32, val)
88 .map_cell_builder_error()?;
89 self.bits_to_write += bit_len;
90 Ok(self)
91 }
92
93 pub fn store_i64(&mut self, bit_len: usize, val: i64) -> Result<&mut Self, TonCellError> {
94 self.bit_writer
95 .write(bit_len as u32, val)
96 .map_cell_builder_error()?;
97 self.bits_to_write += bit_len;
98 Ok(self)
99 }
100
101 pub fn store_uint(&mut self, bit_len: usize, val: &BigUint) -> Result<&mut Self, TonCellError> {
102 let minimum_bits_needed = if val.is_zero() { 1 } else { val.bits() } as usize;
103 if minimum_bits_needed > bit_len {
104 return Err(TonCellError::cell_builder_error(format!(
105 "Value {} doesn't fit in {} bits (takes {} bits)",
106 val, bit_len, minimum_bits_needed
107 )));
108 }
109
110 let value_bytes = val.to_bytes_be();
111 let first_byte_bit_size = bit_len - (value_bytes.len() - 1) * 8;
112
113 for _ in 0..(first_byte_bit_size - 1) / 32 {
114 self.store_u32(32, 0u32)?;
116 }
117
118 if first_byte_bit_size % 32 == 0 {
120 self.store_u32(32, value_bytes[0] as u32)?;
121 } else {
122 self.store_u32(first_byte_bit_size % 32, value_bytes[0] as u32)
123 .map_cell_builder_error()?;
124 }
125
126 for byte in value_bytes.iter().skip(1) {
128 self.store_u8(8, *byte).map_cell_builder_error()?;
129 }
130 Ok(self)
131 }
132
133 pub fn store_int(&mut self, bit_len: usize, val: &BigInt) -> Result<&mut Self, TonCellError> {
134 let (sign, mag) = val.clone().into_parts();
135 let bit_len = bit_len - 1; if bit_len < mag.bits() as usize {
137 return Err(TonCellError::cell_builder_error(format!(
138 "Value {} doesn't fit in {} bits (takes {} bits)",
139 val,
140 bit_len,
141 mag.bits()
142 )));
143 }
144 if sign == Sign::Minus {
145 self.store_byte(1)?;
146 self.store_uint(bit_len, &extend_and_invert_bits(bit_len, &mag)?)?;
147 } else {
148 self.store_byte(0)?;
149 self.store_uint(bit_len, &mag)?;
150 };
151 Ok(self)
152 }
153
154 pub fn store_byte(&mut self, val: u8) -> Result<&mut Self, TonCellError> {
155 self.store_u8(8, val)
156 }
157
158 pub fn store_slice(&mut self, slice: &[u8]) -> Result<&mut Self, TonCellError> {
159 for val in slice {
160 self.store_byte(*val)?;
161 }
162 Ok(self)
163 }
164
165 pub fn store_bits(&mut self, bit_len: usize, slice: &[u8]) -> Result<&mut Self, TonCellError> {
166 let full_bytes = bit_len / 8;
167 self.store_slice(&slice[0..full_bytes])?;
168 let last_byte_len = bit_len % 8;
169 if last_byte_len != 0 {
170 let last_byte = slice[full_bytes] >> (8 - last_byte_len);
171 self.store_u8(last_byte_len, last_byte)?;
172 }
173 Ok(self)
174 }
175
176 pub fn store_string(&mut self, val: &str) -> Result<&mut Self, TonCellError> {
177 self.store_slice(val.as_bytes())
178 }
179
180 pub fn store_coins(&mut self, val: &BigUint) -> Result<&mut Self, TonCellError> {
181 if val.is_zero() {
182 self.store_u8(4, 0)
183 } else {
184 let num_bytes = (val.bits() as usize + 7) / 8;
185 self.store_u8(4, num_bytes as u8)?;
186 self.store_uint(num_bytes * 8, val)
187 }
188 }
189
190 pub fn store_raw_address(&mut self, val: &TonAddress) -> Result<&mut Self, TonCellError> {
192 self.store_u8(2, 0b10u8)?;
193 self.store_bit(false)?;
194 let wc = (val.workchain & 0xff) as u8;
195 self.store_u8(8, wc)?;
196 self.store_slice(&val.hash_part)?;
197 Ok(self)
198 }
199
200 pub fn store_address(&mut self, val: &TonAddress) -> Result<&mut Self, TonCellError> {
202 if val == &TonAddress::NULL {
203 self.store_u8(2, 0)?;
204 } else {
205 self.store_raw_address(val)?;
206 }
207 Ok(self)
208 }
209
210 pub fn store_reference(&mut self, cell: &ArcCell) -> Result<&mut Self, TonCellError> {
214 let ref_count = self.references.len() + 1;
215 if ref_count > 4 {
216 return Err(TonCellError::cell_builder_error(format!(
217 "Cell must contain at most 4 references, got {}",
218 ref_count
219 )));
220 }
221 self.references.push(cell.clone());
222 Ok(self)
223 }
224
225 pub fn store_references(&mut self, refs: &[ArcCell]) -> Result<&mut Self, TonCellError> {
226 for r in refs {
227 self.store_reference(r)?;
228 }
229 Ok(self)
230 }
231
232 pub fn store_child(&mut self, cell: Cell) -> Result<&mut Self, TonCellError> {
236 self.store_reference(&Arc::new(cell))
237 }
238
239 pub fn store_remaining_bits(
240 &mut self,
241 parser: &mut CellParser,
242 ) -> Result<&mut Self, TonCellError> {
243 let num_full_bytes = parser.remaining_bits() / 8;
244 let bytes = parser.load_bytes(num_full_bytes)?;
245 self.store_slice(bytes.as_slice())?;
246 let num_bits = parser.remaining_bits() % 8;
247 let tail = parser.load_u8(num_bits)?;
248 self.store_u8(num_bits, tail)?;
249 Ok(self)
250 }
251
252 pub fn store_cell_data(&mut self, cell: &Cell) -> Result<&mut Self, TonCellError> {
253 let mut parser = cell.parser();
254 self.store_remaining_bits(&mut parser)?;
255 Ok(self)
256 }
257
258 pub fn store_cell(&mut self, cell: &Cell) -> Result<&mut Self, TonCellError> {
259 self.store_cell_data(cell)?;
260 self.store_references(cell.references.as_slice())?;
261 Ok(self)
262 }
263
264 pub fn store_either_cell_or_cell_ref(
266 &mut self,
267 cell: &ArcCell,
268 layout: EitherCellLayout,
269 ) -> Result<&mut Self, TonCellError> {
270 match layout {
271 EitherCellLayout::Native => {
272 if cell.bit_len() < self.remaining_bits() {
273 self.store_bit(false)?;
274 self.store_cell(cell)?;
275 } else {
276 self.store_bit(true)?;
277 self.store_reference(cell)?;
278 }
279 }
280 EitherCellLayout::ToRef => {
281 self.store_bit(true)?;
282 self.store_reference(cell)?;
283 }
284 EitherCellLayout::ToCell => {
285 self.store_bit(false)?;
286 self.store_cell(cell)?;
287 }
288 }
289
290 Ok(self)
291 }
292
293 pub fn store_maybe_cell_ref(
295 &mut self,
296 maybe_cell: &Option<ArcCell>,
297 ) -> Result<&mut Self, TonCellError> {
298 if let Some(cell) = maybe_cell {
299 self.store_bit(true)?;
300 self.store_reference(cell)?;
301 } else {
302 self.store_bit(false)?;
303 }
304
305 Ok(self)
306 }
307
308 pub fn store_dict<K, V>(
309 &mut self,
310 key_len_bits: usize,
311 value_writer: ValWriter<V>,
312 data: HashMap<K, V>,
313 ) -> Result<&mut Self, TonCellError>
314 where
315 BigUint: From<K>,
316 {
317 let dict_builder = DictBuilder::new(key_len_bits, value_writer, data)?;
318 let dict_cell = dict_builder.build()?;
319 self.store_cell(&dict_cell)
320 }
321
322 pub fn remaining_bits(&self) -> usize {
323 MAX_CELL_BITS - self.bits_to_write
324 }
325
326 pub fn build(&mut self) -> Result<Cell, TonCellError> {
327 let mut trailing_zeros = 0;
328 while !self.bit_writer.byte_aligned() {
329 self.bit_writer.write_bit(false).map_cell_builder_error()?;
330 trailing_zeros += 1;
331 }
332
333 if let Some(vec) = self.bit_writer.writer() {
334 let bit_len = vec.len() * 8 - trailing_zeros;
335 if bit_len > MAX_CELL_BITS {
336 return Err(TonCellError::cell_builder_error(format!(
337 "Cell must contain at most {} bits, got {}",
338 MAX_CELL_BITS, bit_len
339 )));
340 }
341 let ref_count = self.references.len();
342 if ref_count > MAX_CELL_REFERENCES {
343 return Err(TonCellError::cell_builder_error(format!(
344 "Cell must contain at most 4 references, got {}",
345 ref_count
346 )));
347 }
348
349 Cell::new(
350 vec.clone(),
351 bit_len,
352 self.references.clone(),
353 self.is_cell_exotic,
354 )
355 } else {
356 Err(TonCellError::CellBuilderError(
357 "Stream is not byte-aligned".to_string(),
358 ))
359 }
360 }
361}
362
363fn extend_and_invert_bits(bits_cnt: usize, src: &BigUint) -> Result<BigUint, TonCellError> {
364 if bits_cnt < src.bits() as usize {
365 return Err(TonCellError::cell_builder_error(format!(
366 "Can't invert bits: value {} doesn't fit in {} bits",
367 src, bits_cnt
368 )));
369 }
370
371 let src_bytes = src.to_bytes_be();
372 let inverted_bytes_cnt = (bits_cnt + 7) / 8;
373 let mut inverted = vec![0xffu8; inverted_bytes_cnt];
374 for (pos, byte) in src_bytes.iter().rev().enumerate() {
376 let inverted_pos = inverted.len() - 1 - pos;
377 inverted[inverted_pos] ^= byte;
378 }
379 let mut inverted_val_bytes = BigUint::from_bytes_be(&inverted)
380 .add(BigUint::one())
381 .to_bytes_be();
382 let leading_zeros = inverted_bytes_cnt * 8 - bits_cnt;
383 inverted_val_bytes[0] &= 0xffu8 >> leading_zeros;
384 Ok(BigUint::from_bytes_be(&inverted_val_bytes))
385}
386
387impl Default for CellBuilder {
388 fn default() -> Self {
389 Self::new()
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use std::collections::HashMap;
396 use std::str::FromStr;
397
398 use num_bigint::{BigInt, BigUint, Sign};
399 use num_traits::Zero;
400
401 use crate::cell::builder::extend_and_invert_bits;
402 use crate::cell::dict::predefined_readers::{key_reader_u8, val_reader_uint};
403 use crate::cell::{CellBuilder, TonCellError};
404 use crate::types::TonAddress;
405
406 #[test]
407 fn test_extend_and_invert_bits() -> Result<(), TonCellError> {
408 let a = BigUint::from(1u8);
409 let b = extend_and_invert_bits(8, &a)?;
410 println!("a: {:0x}", a);
411 println!("b: {:0x}", b);
412 assert_eq!(b, BigUint::from(0xffu8));
413
414 let b = extend_and_invert_bits(16, &a)?;
415 assert_eq!(b, BigUint::from_slice(&[0xffffu32]));
416
417 let b = extend_and_invert_bits(20, &a)?;
418 assert_eq!(b, BigUint::from_slice(&[0xfffffu32]));
419
420 let b = extend_and_invert_bits(8, &a)?;
421 assert_eq!(b, BigUint::from_slice(&[0xffu32]));
422
423 let b = extend_and_invert_bits(9, &a)?;
424 assert_eq!(b, BigUint::from_slice(&[0x1ffu32]));
425
426 assert!(extend_and_invert_bits(3, &BigUint::from(10u32)).is_err());
427 Ok(())
428 }
429
430 #[test]
431 fn write_bit() -> Result<(), TonCellError> {
432 let mut writer = CellBuilder::new();
433 let cell = writer.store_bit(true)?.build()?;
434 assert_eq!(cell.data, [0b1000_0000]);
435 assert_eq!(cell.bit_len, 1);
436 let mut reader = cell.parser();
437 let result = reader.load_bit()?;
438 assert!(result);
439 Ok(())
440 }
441
442 #[test]
443 fn write_u8() -> Result<(), TonCellError> {
444 let value = 234u8;
445 let mut writer = CellBuilder::new();
446 let cell = writer.store_u8(8, value)?.build()?;
447 assert_eq!(cell.data, [0b1110_1010]);
448 assert_eq!(cell.bit_len, 8);
449 let mut reader = cell.parser();
450 let result = reader.load_u8(8)?;
451 assert_eq!(result, value);
452 Ok(())
453 }
454
455 #[test]
456 fn write_u32() -> Result<(), TonCellError> {
457 let value = 0xFAD45AADu32;
458 let mut writer = CellBuilder::new();
459 let cell = writer.store_u32(32, value)?.build()?;
460 assert_eq!(cell.data, [0xFA, 0xD4, 0x5A, 0xAD]);
461 assert_eq!(cell.bit_len, 32);
462 let mut reader = cell.parser();
463 let result = reader.load_u32(32)?;
464 assert_eq!(result, value);
465 Ok(())
466 }
467
468 #[test]
469 fn write_u64() -> Result<(), TonCellError> {
470 let value = 0xFAD45AADAA12FF45;
471 let mut writer = CellBuilder::new();
472 let cell = writer.store_u64(64, value)?.build()?;
473 assert_eq!(cell.data, [0xFA, 0xD4, 0x5A, 0xAD, 0xAA, 0x12, 0xFF, 0x45]);
474 assert_eq!(cell.bit_len, 64);
475 let mut reader = cell.parser();
476 let result = reader.load_u64(64)?;
477 assert_eq!(result, value);
478 Ok(())
479 }
480
481 #[test]
482 fn write_slice() -> Result<(), TonCellError> {
483 let value = [0xFA, 0xD4, 0x5A, 0xAD, 0xAA, 0x12, 0xFF, 0x45];
484 let mut writer = CellBuilder::new();
485 let cell = writer.store_slice(&value)?.build()?;
486 assert_eq!(cell.data, value);
487 assert_eq!(cell.bit_len, 64);
488 let mut reader = cell.parser();
489 let bytes = reader.load_bytes(8)?;
490 assert_eq!(bytes, value);
491 Ok(())
492 }
493
494 #[test]
495 fn write_str() -> Result<(), TonCellError> {
496 let texts = ["hello", "Русский текст", "中华人民共和国", "\u{263A}😃"];
497 for text in texts {
498 let mut writer = CellBuilder::new();
499 let cell = writer.store_string(text)?.build()?;
500 let text_bytes = text.as_bytes();
501 assert_eq!(cell.data, text_bytes);
502 assert_eq!(cell.bit_len, text_bytes.len() * 8);
503 let mut reader = cell.parser();
504 let remaining_bytes = reader.remaining_bytes();
505 let result = reader.load_utf8(remaining_bytes)?;
506 assert_eq!(result, text);
507 }
508 Ok(())
509 }
510
511 #[test]
512 fn write_address() -> Result<(), TonCellError> {
513 let addr = TonAddress::from_base64_url("EQDk2VTvn04SUKJrW7rXahzdF8_Qi6utb0wj43InCu9vdjrR")
514 .unwrap();
515
516 let mut writer = CellBuilder::new();
517 let cell = writer.store_address(&addr)?.build()?;
518 assert_eq!(
519 cell.data,
520 [
521 128, 28, 155, 42, 157, 243, 233, 194, 74, 20, 77, 107, 119, 90, 237, 67, 155, 162,
522 249, 250, 17, 117, 117, 173, 233, 132, 124, 110, 68, 225, 93, 237, 238, 192
523 ]
524 );
525 assert_eq!(cell.bit_len, 2 + 1 + 8 + 32 * 8);
526 let mut reader = cell.parser();
527 let result = reader.load_address()?;
528 assert_eq!(result, addr);
529 Ok(())
530 }
531
532 #[test]
533 fn write_big_int() -> Result<(), TonCellError> {
534 let value = BigInt::from_str("3").unwrap();
535 let mut writer = CellBuilder::new();
536 writer.store_int(33, &value)?;
537 let cell = writer.build()?;
538 println!("cell: {:?}", cell);
539 let written = BigInt::from_bytes_be(Sign::Plus, &cell.data);
540 assert_eq!(written, value);
541
542 let value = BigInt::from_str(
544 "97887266651548624282413032824435501549503168134499591480902563623927645013201",
545 )
546 .unwrap();
547 let mut writer = CellBuilder::new();
548 writer.store_int(257, &value)?;
549 let cell = writer.build()?;
550 println!("cell: {:?}", cell);
551 let written = BigInt::from_bytes_be(Sign::Plus, &cell.data);
552 assert_eq!(written, value);
553
554 let value = BigInt::from_str("-5").unwrap();
555 let mut writer = CellBuilder::new();
556 writer.store_int(5, &value)?;
557 let cell = writer.build()?;
558 println!("cell: {:?}", cell);
559 let written = BigInt::from_bytes_be(Sign::Plus, &cell.data[1..]);
560 let expected = BigInt::from_bytes_be(Sign::Plus, &[0xB0u8]);
561 assert_eq!(written, expected);
562 Ok(())
563 }
564
565 #[test]
566 fn write_load_big_uint() -> Result<(), TonCellError> {
567 let value = BigUint::from_str("3").unwrap();
568 let mut writer = CellBuilder::new();
569 assert!(writer.store_uint(1, &value).is_err());
570 let bits_for_tests = [256, 128, 64, 8];
571
572 for bits_num in bits_for_tests.iter() {
573 writer.store_uint(*bits_num, &value)?;
574 }
575 let cell = writer.build()?;
576 println!("cell: {:?}", cell);
577 let mut cell_parser = cell.parser();
578 for bits_num in bits_for_tests.iter() {
579 let written_value = cell_parser.load_uint(*bits_num)?;
580 assert_eq!(written_value, value);
581 }
582
583 let value = BigUint::from_str(
585 "97887266651548624282413032824435501549503168134499591480902563623927645013201",
586 )
587 .unwrap();
588 let mut writer = CellBuilder::new();
589 assert!(writer.store_uint(255, &value).is_err());
590 let bits_for_tests = [496, 264, 256];
591 for bits_num in bits_for_tests.iter() {
592 writer.store_uint(*bits_num, &value)?;
593 }
594 let cell = writer.build()?;
595 let mut cell_parser = cell.parser();
596 println!("cell: {:?}", cell);
597 for bits_num in bits_for_tests.iter() {
598 let written_value = cell_parser.load_uint(*bits_num)?;
599 assert_eq!(written_value, value);
600 }
601
602 Ok(())
603 }
604
605 #[test]
606 fn test_padding() -> Result<(), TonCellError> {
607 let mut writer = CellBuilder::new();
608
609 let n = BigUint::from(0x55a5f0f0u32);
610
611 writer.store_uint(32, &BigUint::zero())?;
612 writer.store_uint(32, &n)?;
613 writer.store_uint(31, &BigUint::zero())?;
614 writer.store_uint(31, &n)?;
615 writer.store_uint(35, &BigUint::zero())?;
616 writer.store_uint(35, &n)?;
617 let cell = writer.build()?;
618
619 println!("{:?}", cell);
620 assert_eq!(cell.data.len(), 25);
621 assert_eq!(cell.bit_len, 196);
622
623 let mut parser = cell.parser();
624 let result_zero = parser.load_uint(32)?;
625 let result_test_num = parser.load_uint(32)?;
626
627 assert_eq!(result_zero, BigUint::zero());
628 assert_eq!(result_test_num, n);
629 let result_zero = parser.load_uint(31)?;
630 let result_test_num = parser.load_uint(31)?;
631
632 assert_eq!(result_zero, BigUint::zero());
633 assert_eq!(result_test_num, n);
634 let result_zero = parser.load_uint(35)?;
635 let result_test_num = parser.load_uint(35)?;
636
637 assert_eq!(result_zero, BigUint::zero());
638
639 assert_eq!(result_test_num, n);
640 parser.ensure_empty()?;
641
642 Ok(())
643 }
644
645 #[test]
646 fn test_zero_alone() -> Result<(), TonCellError> {
647 let bitlens_to_test = [
648 1, 7, 8, 9, 30, 31, 32, 33, 127, 128, 129, 255, 256, 257, 300,
649 ];
650 for bitlen in bitlens_to_test {
651 let mut writer = CellBuilder::new();
652 writer.store_uint(bitlen, &BigUint::zero())?;
653
654 let cell = writer.build()?;
655
656 println!("{:?}", cell);
657 let taeget_bytelen = (bitlen + 7) / 8;
658 assert_eq!(cell.data.len(), taeget_bytelen);
659
660 assert_eq!(cell.bit_len, bitlen);
661
662 let mut parser = cell.parser();
663 let result_zero = parser.load_uint(bitlen)?;
664
665 assert_eq!(result_zero, BigUint::zero());
666 parser.ensure_empty()?;
667 }
668 Ok(())
669 }
670
671 #[test]
672 fn test_store_dict() -> Result<(), TonCellError> {
673 let mut builder = CellBuilder::new();
674 let mut data = HashMap::new();
675 data.insert(1u8, BigUint::from(2u8));
676 data.insert(3u8, BigUint::from(4u8));
677
678 let value_writer = |writer: &mut CellBuilder, value: BigUint| {
679 writer.store_uint(8, &value)?;
680 Ok(())
681 };
682 builder.store_dict(8, value_writer, data.clone())?;
683 let cell = builder.build()?;
684 let mut parser = cell.parser();
685 let parsed = parser.load_dict(8, key_reader_u8, val_reader_uint)?;
686 assert_eq!(data, parsed);
687 Ok(())
688 }
689}