1 #![cfg_attr(not(feature = "full"), allow(dead_code))] 2 3 //! An intrusive double linked list of data 4 //! 5 //! The data structure supports tracking pinned nodes. Most of the data 6 //! structure's APIs are `unsafe` as they require the caller to ensure the 7 //! specified node is actually contained by the list. 8 9 use core::cell::UnsafeCell; 10 use core::fmt; 11 use core::marker::{PhantomData, PhantomPinned}; 12 use core::mem::ManuallyDrop; 13 use core::ptr::{self, NonNull}; 14 15 /// An intrusive linked list. 16 /// 17 /// Currently, the list is not emptied on drop. It is the caller's 18 /// responsibility to ensure the list is empty before dropping it. 19 pub(crate) struct LinkedList<L, T> { 20 /// Linked list head 21 head: Option<NonNull<T>>, 22 23 /// Linked list tail 24 tail: Option<NonNull<T>>, 25 26 /// Node type marker. 27 _marker: PhantomData<*const L>, 28 } 29 30 unsafe impl<L: Link> Send for LinkedList<L, L::Target> where L::Target: Send {} 31 unsafe impl<L: Link> Sync for LinkedList<L, L::Target> where L::Target: Sync {} 32 33 /// Defines how a type is tracked within a linked list. 34 /// 35 /// In order to support storing a single type within multiple lists, accessing 36 /// the list pointers is decoupled from the entry type. 37 /// 38 /// # Safety 39 /// 40 /// Implementations must guarantee that `Target` types are pinned in memory. In 41 /// other words, when a node is inserted, the value will not be moved as long as 42 /// it is stored in the list. 43 pub(crate) unsafe trait Link { 44 /// Handle to the list entry. 45 /// 46 /// This is usually a pointer-ish type. 47 type Handle; 48 49 /// Node type 50 type Target; 51 52 /// Convert the handle to a raw pointer without consuming the handle as_raw(handle: &Self::Handle) -> NonNull<Self::Target>53 fn as_raw(handle: &Self::Handle) -> NonNull<Self::Target>; 54 55 /// Convert the raw pointer to a handle from_raw(ptr: NonNull<Self::Target>) -> Self::Handle56 unsafe fn from_raw(ptr: NonNull<Self::Target>) -> Self::Handle; 57 58 /// Return the pointers for a node pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>>59 unsafe fn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>>; 60 } 61 62 /// Previous / next pointers 63 pub(crate) struct Pointers<T> { 64 inner: UnsafeCell<PointersInner<T>>, 65 } 66 /// We do not want the compiler to put the `noalias` attribute on mutable 67 /// references to this type, so the type has been made `!Unpin` with a 68 /// `PhantomPinned` field. 69 /// 70 /// Additionally, we never access the `prev` or `next` fields directly, as any 71 /// such access would implicitly involve the creation of a reference to the 72 /// field, which we want to avoid since the fields are not `!Unpin`, and would 73 /// hence be given the `noalias` attribute if we were to do such an access. 74 /// As an alternative to accessing the fields directly, the `Pointers` type 75 /// provides getters and setters for the two fields, and those are implemented 76 /// using raw pointer casts and offsets, which is valid since the struct is 77 /// #[repr(C)]. 78 /// 79 /// See this link for more information: 80 /// https://github.com/rust-lang/rust/pull/82834 81 #[repr(C)] 82 struct PointersInner<T> { 83 /// The previous node in the list. null if there is no previous node. 84 /// 85 /// This field is accessed through pointer manipulation, so it is not dead code. 86 #[allow(dead_code)] 87 prev: Option<NonNull<T>>, 88 89 /// The next node in the list. null if there is no previous node. 90 /// 91 /// This field is accessed through pointer manipulation, so it is not dead code. 92 #[allow(dead_code)] 93 next: Option<NonNull<T>>, 94 95 /// This type is !Unpin due to the heuristic from: 96 /// https://github.com/rust-lang/rust/pull/82834 97 _pin: PhantomPinned, 98 } 99 100 unsafe impl<T: Send> Send for Pointers<T> {} 101 unsafe impl<T: Sync> Sync for Pointers<T> {} 102 103 // ===== impl LinkedList ===== 104 105 impl<L, T> LinkedList<L, T> { 106 /// Creates an empty linked list. new() -> LinkedList<L, T>107 pub(crate) const fn new() -> LinkedList<L, T> { 108 LinkedList { 109 head: None, 110 tail: None, 111 _marker: PhantomData, 112 } 113 } 114 } 115 116 impl<L: Link> LinkedList<L, L::Target> { 117 /// Adds an element first in the list. push_front(&mut self, val: L::Handle)118 pub(crate) fn push_front(&mut self, val: L::Handle) { 119 // The value should not be dropped, it is being inserted into the list 120 let val = ManuallyDrop::new(val); 121 let ptr = L::as_raw(&*val); 122 assert_ne!(self.head, Some(ptr)); 123 unsafe { 124 L::pointers(ptr).as_mut().set_next(self.head); 125 L::pointers(ptr).as_mut().set_prev(None); 126 127 if let Some(head) = self.head { 128 L::pointers(head).as_mut().set_prev(Some(ptr)); 129 } 130 131 self.head = Some(ptr); 132 133 if self.tail.is_none() { 134 self.tail = Some(ptr); 135 } 136 } 137 } 138 139 /// Removes the last element from a list and returns it, or None if it is 140 /// empty. pop_back(&mut self) -> Option<L::Handle>141 pub(crate) fn pop_back(&mut self) -> Option<L::Handle> { 142 unsafe { 143 let last = self.tail?; 144 self.tail = L::pointers(last).as_ref().get_prev(); 145 146 if let Some(prev) = L::pointers(last).as_ref().get_prev() { 147 L::pointers(prev).as_mut().set_next(None); 148 } else { 149 self.head = None 150 } 151 152 L::pointers(last).as_mut().set_prev(None); 153 L::pointers(last).as_mut().set_next(None); 154 155 Some(L::from_raw(last)) 156 } 157 } 158 159 /// Returns whether the linked list does not contain any node is_empty(&self) -> bool160 pub(crate) fn is_empty(&self) -> bool { 161 if self.head.is_some() { 162 return false; 163 } 164 165 assert!(self.tail.is_none()); 166 true 167 } 168 169 /// Removes the specified node from the list 170 /// 171 /// # Safety 172 /// 173 /// The caller **must** ensure that `node` is currently contained by 174 /// `self` or not contained by any other list. remove(&mut self, node: NonNull<L::Target>) -> Option<L::Handle>175 pub(crate) unsafe fn remove(&mut self, node: NonNull<L::Target>) -> Option<L::Handle> { 176 if let Some(prev) = L::pointers(node).as_ref().get_prev() { 177 debug_assert_eq!(L::pointers(prev).as_ref().get_next(), Some(node)); 178 L::pointers(prev) 179 .as_mut() 180 .set_next(L::pointers(node).as_ref().get_next()); 181 } else { 182 if self.head != Some(node) { 183 return None; 184 } 185 186 self.head = L::pointers(node).as_ref().get_next(); 187 } 188 189 if let Some(next) = L::pointers(node).as_ref().get_next() { 190 debug_assert_eq!(L::pointers(next).as_ref().get_prev(), Some(node)); 191 L::pointers(next) 192 .as_mut() 193 .set_prev(L::pointers(node).as_ref().get_prev()); 194 } else { 195 // This might be the last item in the list 196 if self.tail != Some(node) { 197 return None; 198 } 199 200 self.tail = L::pointers(node).as_ref().get_prev(); 201 } 202 203 L::pointers(node).as_mut().set_next(None); 204 L::pointers(node).as_mut().set_prev(None); 205 206 Some(L::from_raw(node)) 207 } 208 } 209 210 impl<L: Link> fmt::Debug for LinkedList<L, L::Target> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 212 f.debug_struct("LinkedList") 213 .field("head", &self.head) 214 .field("tail", &self.tail) 215 .finish() 216 } 217 } 218 219 #[cfg(any( 220 feature = "fs", 221 all(unix, feature = "process"), 222 feature = "signal", 223 feature = "sync", 224 ))] 225 impl<L: Link> LinkedList<L, L::Target> { last(&self) -> Option<&L::Target>226 pub(crate) fn last(&self) -> Option<&L::Target> { 227 let tail = self.tail.as_ref()?; 228 unsafe { Some(&*tail.as_ptr()) } 229 } 230 } 231 232 impl<L: Link> Default for LinkedList<L, L::Target> { default() -> Self233 fn default() -> Self { 234 Self::new() 235 } 236 } 237 238 // ===== impl Iter ===== 239 240 cfg_rt_multi_thread! { 241 pub(crate) struct Iter<'a, T: Link> { 242 curr: Option<NonNull<T::Target>>, 243 _p: core::marker::PhantomData<&'a T>, 244 } 245 246 impl<L: Link> LinkedList<L, L::Target> { 247 pub(crate) fn iter(&self) -> Iter<'_, L> { 248 Iter { 249 curr: self.head, 250 _p: core::marker::PhantomData, 251 } 252 } 253 } 254 255 impl<'a, T: Link> Iterator for Iter<'a, T> { 256 type Item = &'a T::Target; 257 258 fn next(&mut self) -> Option<&'a T::Target> { 259 let curr = self.curr?; 260 // safety: the pointer references data contained by the list 261 self.curr = unsafe { T::pointers(curr).as_ref() }.get_next(); 262 263 // safety: the value is still owned by the linked list. 264 Some(unsafe { &*curr.as_ptr() }) 265 } 266 } 267 } 268 269 // ===== impl DrainFilter ===== 270 271 cfg_io_readiness! { 272 pub(crate) struct DrainFilter<'a, T: Link, F> { 273 list: &'a mut LinkedList<T, T::Target>, 274 filter: F, 275 curr: Option<NonNull<T::Target>>, 276 } 277 278 impl<T: Link> LinkedList<T, T::Target> { 279 pub(crate) fn drain_filter<F>(&mut self, filter: F) -> DrainFilter<'_, T, F> 280 where 281 F: FnMut(&mut T::Target) -> bool, 282 { 283 let curr = self.head; 284 DrainFilter { 285 curr, 286 filter, 287 list: self, 288 } 289 } 290 } 291 292 impl<'a, T, F> Iterator for DrainFilter<'a, T, F> 293 where 294 T: Link, 295 F: FnMut(&mut T::Target) -> bool, 296 { 297 type Item = T::Handle; 298 299 fn next(&mut self) -> Option<Self::Item> { 300 while let Some(curr) = self.curr { 301 // safety: the pointer references data contained by the list 302 self.curr = unsafe { T::pointers(curr).as_ref() }.get_next(); 303 304 // safety: the value is still owned by the linked list. 305 if (self.filter)(unsafe { &mut *curr.as_ptr() }) { 306 return unsafe { self.list.remove(curr) }; 307 } 308 } 309 310 None 311 } 312 } 313 } 314 315 // ===== impl Pointers ===== 316 317 impl<T> Pointers<T> { 318 /// Create a new set of empty pointers new() -> Pointers<T>319 pub(crate) fn new() -> Pointers<T> { 320 Pointers { 321 inner: UnsafeCell::new(PointersInner { 322 prev: None, 323 next: None, 324 _pin: PhantomPinned, 325 }), 326 } 327 } 328 get_prev(&self) -> Option<NonNull<T>>329 fn get_prev(&self) -> Option<NonNull<T>> { 330 // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. 331 unsafe { 332 let inner = self.inner.get(); 333 let prev = inner as *const Option<NonNull<T>>; 334 ptr::read(prev) 335 } 336 } get_next(&self) -> Option<NonNull<T>>337 fn get_next(&self) -> Option<NonNull<T>> { 338 // SAFETY: next is the second field in PointersInner, which is #[repr(C)]. 339 unsafe { 340 let inner = self.inner.get(); 341 let prev = inner as *const Option<NonNull<T>>; 342 let next = prev.add(1); 343 ptr::read(next) 344 } 345 } 346 set_prev(&mut self, value: Option<NonNull<T>>)347 fn set_prev(&mut self, value: Option<NonNull<T>>) { 348 // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. 349 unsafe { 350 let inner = self.inner.get(); 351 let prev = inner as *mut Option<NonNull<T>>; 352 ptr::write(prev, value); 353 } 354 } set_next(&mut self, value: Option<NonNull<T>>)355 fn set_next(&mut self, value: Option<NonNull<T>>) { 356 // SAFETY: next is the second field in PointersInner, which is #[repr(C)]. 357 unsafe { 358 let inner = self.inner.get(); 359 let prev = inner as *mut Option<NonNull<T>>; 360 let next = prev.add(1); 361 ptr::write(next, value); 362 } 363 } 364 } 365 366 impl<T> fmt::Debug for Pointers<T> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result367 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 368 let prev = self.get_prev(); 369 let next = self.get_next(); 370 f.debug_struct("Pointers") 371 .field("prev", &prev) 372 .field("next", &next) 373 .finish() 374 } 375 } 376 377 #[cfg(test)] 378 #[cfg(not(loom))] 379 mod tests { 380 use super::*; 381 382 use std::pin::Pin; 383 384 #[derive(Debug)] 385 struct Entry { 386 pointers: Pointers<Entry>, 387 val: i32, 388 } 389 390 unsafe impl<'a> Link for &'a Entry { 391 type Handle = Pin<&'a Entry>; 392 type Target = Entry; 393 as_raw(handle: &Pin<&'_ Entry>) -> NonNull<Entry>394 fn as_raw(handle: &Pin<&'_ Entry>) -> NonNull<Entry> { 395 NonNull::from(handle.get_ref()) 396 } 397 from_raw(ptr: NonNull<Entry>) -> Pin<&'a Entry>398 unsafe fn from_raw(ptr: NonNull<Entry>) -> Pin<&'a Entry> { 399 Pin::new_unchecked(&*ptr.as_ptr()) 400 } 401 pointers(mut target: NonNull<Entry>) -> NonNull<Pointers<Entry>>402 unsafe fn pointers(mut target: NonNull<Entry>) -> NonNull<Pointers<Entry>> { 403 NonNull::from(&mut target.as_mut().pointers) 404 } 405 } 406 entry(val: i32) -> Pin<Box<Entry>>407 fn entry(val: i32) -> Pin<Box<Entry>> { 408 Box::pin(Entry { 409 pointers: Pointers::new(), 410 val, 411 }) 412 } 413 ptr(r: &Pin<Box<Entry>>) -> NonNull<Entry>414 fn ptr(r: &Pin<Box<Entry>>) -> NonNull<Entry> { 415 r.as_ref().get_ref().into() 416 } 417 collect_list(list: &mut LinkedList<&'_ Entry, <&'_ Entry as Link>::Target>) -> Vec<i32>418 fn collect_list(list: &mut LinkedList<&'_ Entry, <&'_ Entry as Link>::Target>) -> Vec<i32> { 419 let mut ret = vec![]; 420 421 while let Some(entry) = list.pop_back() { 422 ret.push(entry.val); 423 } 424 425 ret 426 } 427 push_all<'a>( list: &mut LinkedList<&'a Entry, <&'_ Entry as Link>::Target>, entries: &[Pin<&'a Entry>], )428 fn push_all<'a>( 429 list: &mut LinkedList<&'a Entry, <&'_ Entry as Link>::Target>, 430 entries: &[Pin<&'a Entry>], 431 ) { 432 for entry in entries.iter() { 433 list.push_front(*entry); 434 } 435 } 436 437 macro_rules! assert_clean { 438 ($e:ident) => {{ 439 assert!($e.pointers.get_next().is_none()); 440 assert!($e.pointers.get_prev().is_none()); 441 }}; 442 } 443 444 macro_rules! assert_ptr_eq { 445 ($a:expr, $b:expr) => {{ 446 // Deal with mapping a Pin<&mut T> -> Option<NonNull<T>> 447 assert_eq!(Some($a.as_ref().get_ref().into()), $b) 448 }}; 449 } 450 451 #[test] const_new()452 fn const_new() { 453 const _: LinkedList<&Entry, <&Entry as Link>::Target> = LinkedList::new(); 454 } 455 456 #[test] push_and_drain()457 fn push_and_drain() { 458 let a = entry(5); 459 let b = entry(7); 460 let c = entry(31); 461 462 let mut list = LinkedList::new(); 463 assert!(list.is_empty()); 464 465 list.push_front(a.as_ref()); 466 assert!(!list.is_empty()); 467 list.push_front(b.as_ref()); 468 list.push_front(c.as_ref()); 469 470 let items: Vec<i32> = collect_list(&mut list); 471 assert_eq!([5, 7, 31].to_vec(), items); 472 473 assert!(list.is_empty()); 474 } 475 476 #[test] push_pop_push_pop()477 fn push_pop_push_pop() { 478 let a = entry(5); 479 let b = entry(7); 480 481 let mut list = LinkedList::<&Entry, <&Entry as Link>::Target>::new(); 482 483 list.push_front(a.as_ref()); 484 485 let entry = list.pop_back().unwrap(); 486 assert_eq!(5, entry.val); 487 assert!(list.is_empty()); 488 489 list.push_front(b.as_ref()); 490 491 let entry = list.pop_back().unwrap(); 492 assert_eq!(7, entry.val); 493 494 assert!(list.is_empty()); 495 assert!(list.pop_back().is_none()); 496 } 497 498 #[test] remove_by_address()499 fn remove_by_address() { 500 let a = entry(5); 501 let b = entry(7); 502 let c = entry(31); 503 504 unsafe { 505 // Remove first 506 let mut list = LinkedList::new(); 507 508 push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]); 509 assert!(list.remove(ptr(&a)).is_some()); 510 assert_clean!(a); 511 // `a` should be no longer there and can't be removed twice 512 assert!(list.remove(ptr(&a)).is_none()); 513 assert!(!list.is_empty()); 514 515 assert!(list.remove(ptr(&b)).is_some()); 516 assert_clean!(b); 517 // `b` should be no longer there and can't be removed twice 518 assert!(list.remove(ptr(&b)).is_none()); 519 assert!(!list.is_empty()); 520 521 assert!(list.remove(ptr(&c)).is_some()); 522 assert_clean!(c); 523 // `b` should be no longer there and can't be removed twice 524 assert!(list.remove(ptr(&c)).is_none()); 525 assert!(list.is_empty()); 526 } 527 528 unsafe { 529 // Remove middle 530 let mut list = LinkedList::new(); 531 532 push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]); 533 534 assert!(list.remove(ptr(&a)).is_some()); 535 assert_clean!(a); 536 537 assert_ptr_eq!(b, list.head); 538 assert_ptr_eq!(c, b.pointers.get_next()); 539 assert_ptr_eq!(b, c.pointers.get_prev()); 540 541 let items = collect_list(&mut list); 542 assert_eq!([31, 7].to_vec(), items); 543 } 544 545 unsafe { 546 // Remove middle 547 let mut list = LinkedList::new(); 548 549 push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]); 550 551 assert!(list.remove(ptr(&b)).is_some()); 552 assert_clean!(b); 553 554 assert_ptr_eq!(c, a.pointers.get_next()); 555 assert_ptr_eq!(a, c.pointers.get_prev()); 556 557 let items = collect_list(&mut list); 558 assert_eq!([31, 5].to_vec(), items); 559 } 560 561 unsafe { 562 // Remove last 563 // Remove middle 564 let mut list = LinkedList::new(); 565 566 push_all(&mut list, &[c.as_ref(), b.as_ref(), a.as_ref()]); 567 568 assert!(list.remove(ptr(&c)).is_some()); 569 assert_clean!(c); 570 571 assert!(b.pointers.get_next().is_none()); 572 assert_ptr_eq!(b, list.tail); 573 574 let items = collect_list(&mut list); 575 assert_eq!([7, 5].to_vec(), items); 576 } 577 578 unsafe { 579 // Remove first of two 580 let mut list = LinkedList::new(); 581 582 push_all(&mut list, &[b.as_ref(), a.as_ref()]); 583 584 assert!(list.remove(ptr(&a)).is_some()); 585 586 assert_clean!(a); 587 588 // a should be no longer there and can't be removed twice 589 assert!(list.remove(ptr(&a)).is_none()); 590 591 assert_ptr_eq!(b, list.head); 592 assert_ptr_eq!(b, list.tail); 593 594 assert!(b.pointers.get_next().is_none()); 595 assert!(b.pointers.get_prev().is_none()); 596 597 let items = collect_list(&mut list); 598 assert_eq!([7].to_vec(), items); 599 } 600 601 unsafe { 602 // Remove last of two 603 let mut list = LinkedList::new(); 604 605 push_all(&mut list, &[b.as_ref(), a.as_ref()]); 606 607 assert!(list.remove(ptr(&b)).is_some()); 608 609 assert_clean!(b); 610 611 assert_ptr_eq!(a, list.head); 612 assert_ptr_eq!(a, list.tail); 613 614 assert!(a.pointers.get_next().is_none()); 615 assert!(a.pointers.get_prev().is_none()); 616 617 let items = collect_list(&mut list); 618 assert_eq!([5].to_vec(), items); 619 } 620 621 unsafe { 622 // Remove last item 623 let mut list = LinkedList::new(); 624 625 push_all(&mut list, &[a.as_ref()]); 626 627 assert!(list.remove(ptr(&a)).is_some()); 628 assert_clean!(a); 629 630 assert!(list.head.is_none()); 631 assert!(list.tail.is_none()); 632 let items = collect_list(&mut list); 633 assert!(items.is_empty()); 634 } 635 636 unsafe { 637 // Remove missing 638 let mut list = LinkedList::<&Entry, <&Entry as Link>::Target>::new(); 639 640 list.push_front(b.as_ref()); 641 list.push_front(a.as_ref()); 642 643 assert!(list.remove(ptr(&c)).is_none()); 644 } 645 } 646 647 #[test] iter()648 fn iter() { 649 let a = entry(5); 650 let b = entry(7); 651 652 let mut list = LinkedList::<&Entry, <&Entry as Link>::Target>::new(); 653 654 assert_eq!(0, list.iter().count()); 655 656 list.push_front(a.as_ref()); 657 list.push_front(b.as_ref()); 658 659 let mut i = list.iter(); 660 assert_eq!(7, i.next().unwrap().val); 661 assert_eq!(5, i.next().unwrap().val); 662 assert!(i.next().is_none()); 663 } 664 665 proptest::proptest! { 666 #[test] 667 fn fuzz_linked_list(ops: Vec<usize>) { 668 run_fuzz(ops); 669 } 670 } 671 run_fuzz(ops: Vec<usize>)672 fn run_fuzz(ops: Vec<usize>) { 673 use std::collections::VecDeque; 674 675 #[derive(Debug)] 676 enum Op { 677 Push, 678 Pop, 679 Remove(usize), 680 } 681 682 let ops = ops 683 .iter() 684 .map(|i| match i % 3 { 685 0 => Op::Push, 686 1 => Op::Pop, 687 2 => Op::Remove(i / 3), 688 _ => unreachable!(), 689 }) 690 .collect::<Vec<_>>(); 691 692 let mut ll = LinkedList::<&Entry, <&Entry as Link>::Target>::new(); 693 let mut reference = VecDeque::new(); 694 695 let entries: Vec<_> = (0..ops.len()).map(|i| entry(i as i32)).collect(); 696 697 for (i, op) in ops.iter().enumerate() { 698 match op { 699 Op::Push => { 700 reference.push_front(i as i32); 701 assert_eq!(entries[i].val, i as i32); 702 703 ll.push_front(entries[i].as_ref()); 704 } 705 Op::Pop => { 706 if reference.is_empty() { 707 assert!(ll.is_empty()); 708 continue; 709 } 710 711 let v = reference.pop_back(); 712 assert_eq!(v, ll.pop_back().map(|v| v.val)); 713 } 714 Op::Remove(n) => { 715 if reference.is_empty() { 716 assert!(ll.is_empty()); 717 continue; 718 } 719 720 let idx = n % reference.len(); 721 let expect = reference.remove(idx).unwrap(); 722 723 unsafe { 724 let entry = ll.remove(ptr(&entries[expect as usize])).unwrap(); 725 assert_eq!(expect, entry.val); 726 } 727 } 728 } 729 } 730 } 731 } 732