OLD | NEW |
1 /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ | 1 /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ |
2 /* | 2 /* |
3 * Copyright (c) 2006 INRIA | 3 * Copyright (c) 2006 INRIA |
| 4 * Copyright (c) 2009 MIRKO BANCHI |
4 * | 5 * |
5 * This program is free software; you can redistribute it and/or modify | 6 * This program is free software; you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 2 as· | 7 * it under the terms of the GNU General Public License version 2 as· |
7 * published by the Free Software Foundation; | 8 * published by the Free Software Foundation; |
8 * | 9 * |
9 * This program is distributed in the hope that it will be useful, | 10 * This program is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 13 * GNU General Public License for more details. |
13 * | 14 * |
14 * You should have received a copy of the GNU General Public License | 15 * You should have received a copy of the GNU General Public License |
15 * along with this program; if not, write to the Free Software | 16 * along with this program; if not, write to the Free Software |
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
17 * | 18 * |
18 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr> | 19 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr> |
| 20 * Author: Mirko Banchi <mk.banchi@gmail.com> |
19 */ | 21 */ |
20 #include "mgt-headers.h" | 22 #include "mgt-headers.h" |
21 #include "ns3/simulator.h" | 23 #include "ns3/simulator.h" |
22 #include "ns3/assert.h" | 24 #include "ns3/assert.h" |
23 | 25 |
24 namespace ns3 { | 26 namespace ns3 { |
25 | 27 |
26 /*********************************************************** | 28 /*********************************************************** |
27 * Probe Request | 29 * Probe Request |
28 ***********************************************************/ | 30 ***********************************************************/ |
(...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 { | 400 { |
399 } | 401 } |
400 void | 402 void |
401 WifiActionHeader::SetAction (WifiActionHeader::CategoryValue type, | 403 WifiActionHeader::SetAction (WifiActionHeader::CategoryValue type, |
402 WifiActionHeader::ActionValue action) | 404 WifiActionHeader::ActionValue action) |
403 { | 405 { |
404 m_category = type; | 406 m_category = type; |
405 | 407 |
406 switch (type) | 408 switch (type) |
407 { | 409 { |
| 410 case BLOCK_ACK: |
| 411 { |
| 412 m_actionValue = action.blockAck; |
| 413 break; |
| 414 } |
408 case MESH_PEERING_MGT: | 415 case MESH_PEERING_MGT: |
409 { | 416 { |
410 m_actionValue = action.peerLink; | 417 m_actionValue = action.peerLink; |
411 break; | 418 break; |
412 } | 419 } |
413 case MESH_PATH_SELECTION: | 420 case MESH_PATH_SELECTION: |
414 { | 421 { |
415 m_actionValue = action.pathSelection; | 422 m_actionValue = action.pathSelection; |
416 break; | 423 break; |
417 } | 424 } |
418 case MESH_LINK_METRIC: | 425 case MESH_LINK_METRIC: |
419 case MESH_INTERWORKING: | 426 case MESH_INTERWORKING: |
420 case MESH_RESOURCE_COORDINATION: | 427 case MESH_RESOURCE_COORDINATION: |
421 case MESH_PROXY_FORWARDING: | 428 case MESH_PROXY_FORWARDING: |
422 break; | 429 break; |
423 } | 430 } |
424 } | 431 } |
425 WifiActionHeader::CategoryValue | 432 WifiActionHeader::CategoryValue |
426 WifiActionHeader::GetCategory () | 433 WifiActionHeader::GetCategory () |
427 { | 434 { |
428 switch (m_category) | 435 switch (m_category) |
429 { | 436 { |
| 437 case BLOCK_ACK: |
| 438 return BLOCK_ACK; |
430 case MESH_PEERING_MGT: | 439 case MESH_PEERING_MGT: |
431 return MESH_PEERING_MGT; | 440 return MESH_PEERING_MGT; |
432 case MESH_LINK_METRIC: | 441 case MESH_LINK_METRIC: |
433 return MESH_LINK_METRIC; | 442 return MESH_LINK_METRIC; |
434 case MESH_PATH_SELECTION: | 443 case MESH_PATH_SELECTION: |
435 return MESH_PATH_SELECTION; | 444 return MESH_PATH_SELECTION; |
436 case MESH_INTERWORKING: | 445 case MESH_INTERWORKING: |
437 return MESH_INTERWORKING; | 446 return MESH_INTERWORKING; |
438 case MESH_RESOURCE_COORDINATION: | 447 case MESH_RESOURCE_COORDINATION: |
439 return MESH_RESOURCE_COORDINATION; | 448 return MESH_RESOURCE_COORDINATION; |
440 case MESH_PROXY_FORWARDING: | 449 case MESH_PROXY_FORWARDING: |
441 return MESH_PROXY_FORWARDING; | 450 return MESH_PROXY_FORWARDING; |
442 default: | 451 default: |
443 NS_FATAL_ERROR ("Unknown action value"); | 452 NS_FATAL_ERROR ("Unknown action value"); |
444 return MESH_PEERING_MGT; | 453 return MESH_PEERING_MGT; |
445 } | 454 } |
446 } | 455 } |
447 WifiActionHeader::ActionValue | 456 WifiActionHeader::ActionValue |
448 WifiActionHeader::GetAction () | 457 WifiActionHeader::GetAction () |
449 { | 458 { |
450 ActionValue retval; | 459 ActionValue retval; |
451 retval.peerLink = PEER_LINK_OPEN; // Needs to be initialized to something to q
uiet valgrind in default cases | 460 retval.peerLink = PEER_LINK_OPEN; // Needs to be initialized to something to q
uiet valgrind in default cases |
452 switch (m_category) | 461 switch (m_category) |
453 { | 462 { |
| 463 case BLOCK_ACK: |
| 464 switch (m_actionValue) |
| 465 { |
| 466 case BLOCK_ACK_ADDBA_REQUEST: |
| 467 retval.blockAck = BLOCK_ACK_ADDBA_REQUEST; |
| 468 return retval; |
| 469 case BLOCK_ACK_ADDBA_RESPONSE: |
| 470 retval.blockAck = BLOCK_ACK_ADDBA_RESPONSE; |
| 471 return retval; |
| 472 case BLOCK_ACK_DELBA: |
| 473 retval.blockAck = BLOCK_ACK_DELBA; |
| 474 return retval; |
| 475 } |
454 case MESH_PEERING_MGT: | 476 case MESH_PEERING_MGT: |
455 switch (m_actionValue) | 477 switch (m_actionValue) |
456 { | 478 { |
457 case PEER_LINK_OPEN: | 479 case PEER_LINK_OPEN: |
458 retval.peerLink = PEER_LINK_OPEN; | 480 retval.peerLink = PEER_LINK_OPEN; |
459 return retval; | 481 return retval; |
460 case PEER_LINK_CONFIRM: | 482 case PEER_LINK_CONFIRM: |
461 retval.peerLink = PEER_LINK_CONFIRM; | 483 retval.peerLink = PEER_LINK_CONFIRM; |
462 return retval; | 484 return retval; |
463 case PEER_LINK_CLOSE: | 485 case PEER_LINK_CLOSE: |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 } | 543 } |
522 uint32_t | 544 uint32_t |
523 WifiActionHeader::Deserialize (Buffer::Iterator start) | 545 WifiActionHeader::Deserialize (Buffer::Iterator start) |
524 { | 546 { |
525 Buffer::Iterator i = start; | 547 Buffer::Iterator i = start; |
526 m_category = i.ReadU8 (); | 548 m_category = i.ReadU8 (); |
527 m_actionValue = i.ReadU8 (); | 549 m_actionValue = i.ReadU8 (); |
528 return i.GetDistanceFrom (start); | 550 return i.GetDistanceFrom (start); |
529 } | 551 } |
530 | 552 |
| 553 /*************************************************** |
| 554 * ADDBARequest |
| 555 ****************************************************/ |
| 556 |
| 557 NS_OBJECT_ENSURE_REGISTERED (MgtAddBaRequestHeader); |
| 558 |
| 559 MgtAddBaRequestHeader::MgtAddBaRequestHeader () |
| 560 : m_dialogToken (1), |
| 561 m_amsduSupport (1), |
| 562 m_bufferSize (0) |
| 563 {} |
| 564 |
| 565 TypeId |
| 566 MgtAddBaRequestHeader::GetTypeId (void) |
| 567 { |
| 568 static TypeId tid = TypeId ("ns3::MgtAddBaRequestHeader") |
| 569 .SetParent<Header> () |
| 570 .AddConstructor<MgtAddBaRequestHeader> (); |
| 571 ; |
| 572 return tid; |
| 573 } |
| 574 |
| 575 TypeId |
| 576 MgtAddBaRequestHeader::GetInstanceTypeId (void) const |
| 577 { |
| 578 return GetTypeId (); |
| 579 } |
| 580 |
| 581 void |
| 582 MgtAddBaRequestHeader::Print (std::ostream &os) const |
| 583 {} |
| 584 |
| 585 uint32_t |
| 586 MgtAddBaRequestHeader::GetSerializedSize (void) const |
| 587 { |
| 588 uint32_t size = 0; |
| 589 size += 1; //Dialog token |
| 590 size += 2; //Block ack parameter set |
| 591 size += 2; //Block ack timeout value |
| 592 size += 2; //Starting sequence control |
| 593 return size; |
| 594 } |
| 595 |
| 596 void |
| 597 MgtAddBaRequestHeader::Serialize (Buffer::Iterator start) const |
| 598 { |
| 599 Buffer::Iterator i = start; |
| 600 i.WriteU8 (m_dialogToken); |
| 601 i.WriteHtolsbU16 (GetParameterSet ()); |
| 602 i.WriteHtolsbU16 (m_timeoutValue); |
| 603 i.WriteHtolsbU16 (GetStartingSequenceControl ()); |
| 604 } |
| 605 |
| 606 uint32_t |
| 607 MgtAddBaRequestHeader::Deserialize (Buffer::Iterator start) |
| 608 { |
| 609 Buffer::Iterator i = start; |
| 610 m_dialogToken = i.ReadU8 (); |
| 611 SetParameterSet (i.ReadLsbtohU16 ()); |
| 612 m_timeoutValue = i.ReadLsbtohU16 (); |
| 613 SetStartingSequenceControl (i.ReadLsbtohU16 ()); |
| 614 return i.GetDistanceFrom (start); |
| 615 } |
| 616 |
| 617 void |
| 618 MgtAddBaRequestHeader::SetDelayedBlockAck () |
| 619 { |
| 620 m_policy = 0; |
| 621 } |
| 622 |
| 623 void |
| 624 MgtAddBaRequestHeader::SetImmediateBlockAck () |
| 625 { |
| 626 m_policy = 1; |
| 627 } |
| 628 ·· |
| 629 void |
| 630 MgtAddBaRequestHeader::SetTid (uint8_t tid) |
| 631 { |
| 632 NS_ASSERT (tid < 16); |
| 633 m_tid = tid; |
| 634 } |
| 635 |
| 636 void |
| 637 MgtAddBaRequestHeader::SetTimeout (uint16_t timeout) |
| 638 { |
| 639 m_timeoutValue = timeout; |
| 640 } |
| 641 |
| 642 void |
| 643 MgtAddBaRequestHeader::SetBufferSize (uint16_t size) |
| 644 { |
| 645 m_bufferSize = size; |
| 646 } |
| 647 |
| 648 void |
| 649 MgtAddBaRequestHeader::SetStartingSequence (uint16_t seq) |
| 650 { |
| 651 m_startingSeq = seq; |
| 652 } |
| 653 |
| 654 void |
| 655 MgtAddBaRequestHeader::SetAmsduSupport (bool supported) |
| 656 { |
| 657 m_amsduSupport = supported; |
| 658 } |
| 659 |
| 660 uint8_t |
| 661 MgtAddBaRequestHeader::GetTid (void) const |
| 662 { |
| 663 return m_tid; |
| 664 } |
| 665 |
| 666 bool |
| 667 MgtAddBaRequestHeader::IsImmediateBlockAck (void) const |
| 668 { |
| 669 return (m_policy == 1)?true:false; |
| 670 } |
| 671 |
| 672 uint16_t |
| 673 MgtAddBaRequestHeader::GetTimeout (void) const |
| 674 { |
| 675 return m_timeoutValue; |
| 676 } |
| 677 |
| 678 uint16_t |
| 679 MgtAddBaRequestHeader::GetBufferSize (void) const |
| 680 { |
| 681 return m_bufferSize; |
| 682 } |
| 683 |
| 684 bool |
| 685 MgtAddBaRequestHeader::IsAmsduSupported (void) const |
| 686 { |
| 687 return (m_amsduSupport == 1)?true:false; |
| 688 } |
| 689 |
| 690 uint16_t |
| 691 MgtAddBaRequestHeader::GetStartingSequence (void) const |
| 692 { |
| 693 return m_startingSeq; |
| 694 } |
| 695 |
| 696 uint16_t |
| 697 MgtAddBaRequestHeader::GetStartingSequenceControl (void) const |
| 698 { |
| 699 return (m_startingSeq << 4) & 0xfff0; |
| 700 } |
| 701 |
| 702 void |
| 703 MgtAddBaRequestHeader::SetStartingSequenceControl (uint16_t seqControl) |
| 704 { |
| 705 m_startingSeq = (seqControl >> 4) & 0x0fff; |
| 706 } |
| 707 |
| 708 uint16_t |
| 709 MgtAddBaRequestHeader::GetParameterSet (void) const |
| 710 { |
| 711 uint16_t res = 0; |
| 712 res |= m_amsduSupport; |
| 713 res |= m_policy << 1; |
| 714 res |= m_tid << 2; |
| 715 res |= m_bufferSize << 6; |
| 716 return res; |
| 717 } |
| 718 |
| 719 void |
| 720 MgtAddBaRequestHeader::SetParameterSet (uint16_t params) |
| 721 { |
| 722 m_amsduSupport = (params) & 0x01; |
| 723 m_policy = (params >> 1) & 0x01; |
| 724 m_tid = (params >> 2) & 0x0f; |
| 725 m_bufferSize = (params >> 6) & 0x03ff; |
| 726 } |
| 727 |
| 728 /*************************************************** |
| 729 * ADDBAResponse |
| 730 ****************************************************/ |
| 731 |
| 732 NS_OBJECT_ENSURE_REGISTERED (MgtAddBaResponseHeader); |
| 733 |
| 734 MgtAddBaResponseHeader::MgtAddBaResponseHeader () |
| 735 : m_dialogToken (1), |
| 736 m_amsduSupport (1), |
| 737 m_bufferSize (0) |
| 738 {} |
| 739 |
| 740 TypeId |
| 741 MgtAddBaResponseHeader::GetTypeId () |
| 742 { |
| 743 static TypeId tid = TypeId ("ns3::MgtAddBaResponseHeader") |
| 744 .SetParent<Header> () |
| 745 .AddConstructor<MgtAddBaResponseHeader> () |
| 746 ; |
| 747 return tid; |
| 748 } |
| 749 |
| 750 TypeId |
| 751 MgtAddBaResponseHeader::GetInstanceTypeId (void) const |
| 752 { |
| 753 return GetTypeId (); |
| 754 } |
| 755 |
| 756 void |
| 757 MgtAddBaResponseHeader::Print (std::ostream &os) const |
| 758 { |
| 759 os <<"status code="<<m_code; |
| 760 } |
| 761 |
| 762 uint32_t |
| 763 MgtAddBaResponseHeader::GetSerializedSize (void) const |
| 764 { |
| 765 uint32_t size = 0; |
| 766 size += 1; //Dialog token |
| 767 size += m_code.GetSerializedSize (); //Status code |
| 768 size += 2; //Block ack parameter set |
| 769 size += 2; //Block ack timeout value |
| 770 return size; |
| 771 } |
| 772 |
| 773 void |
| 774 MgtAddBaResponseHeader::Serialize (Buffer::Iterator start) const |
| 775 { |
| 776 Buffer::Iterator i = start; |
| 777 i.WriteU8 (m_dialogToken); |
| 778 i = m_code.Serialize (i); |
| 779 i.WriteHtolsbU16 (GetParameterSet ()); |
| 780 i.WriteHtolsbU16 (m_timeoutValue); |
| 781 } |
| 782 |
| 783 uint32_t |
| 784 MgtAddBaResponseHeader::Deserialize (Buffer::Iterator start) |
| 785 { |
| 786 Buffer::Iterator i = start; |
| 787 m_dialogToken = i.ReadU8 (); |
| 788 i = m_code.Deserialize (i); |
| 789 SetParameterSet (i.ReadLsbtohU16 ()); |
| 790 m_timeoutValue = i.ReadLsbtohU16 (); |
| 791 return i.GetDistanceFrom (start); |
| 792 } |
| 793 |
| 794 void |
| 795 MgtAddBaResponseHeader::SetDelayedBlockAck () |
| 796 { |
| 797 m_policy = 0; |
| 798 } |
| 799 |
| 800 void |
| 801 MgtAddBaResponseHeader::SetImmediateBlockAck () |
| 802 { |
| 803 m_policy = 1; |
| 804 } |
| 805 |
| 806 void |
| 807 MgtAddBaResponseHeader::SetTid (uint8_t tid) |
| 808 { |
| 809 NS_ASSERT (tid < 16); |
| 810 m_tid = tid; |
| 811 } |
| 812 |
| 813 void |
| 814 MgtAddBaResponseHeader::SetTimeout (uint16_t timeout) |
| 815 { |
| 816 m_timeoutValue = timeout; |
| 817 } |
| 818 |
| 819 void |
| 820 MgtAddBaResponseHeader::SetBufferSize (uint16_t size) |
| 821 { |
| 822 m_bufferSize = size; |
| 823 } |
| 824 |
| 825 void |
| 826 MgtAddBaResponseHeader::SetStatusCode (StatusCode code) |
| 827 { |
| 828 m_code = code; |
| 829 } |
| 830 |
| 831 void |
| 832 MgtAddBaResponseHeader::SetAmsduSupport (bool supported) |
| 833 { |
| 834 m_amsduSupport = supported; |
| 835 } |
| 836 |
| 837 StatusCode |
| 838 MgtAddBaResponseHeader::GetStatusCode (void) const |
| 839 { |
| 840 return m_code; |
| 841 } |
| 842 |
| 843 uint8_t |
| 844 MgtAddBaResponseHeader::GetTid (void) const |
| 845 { |
| 846 return m_tid; |
| 847 } |
| 848 |
| 849 bool |
| 850 MgtAddBaResponseHeader::IsImmediateBlockAck (void) const |
| 851 { |
| 852 return (m_policy == 1)?true:false; |
| 853 } |
| 854 |
| 855 uint16_t |
| 856 MgtAddBaResponseHeader::GetTimeout (void) const |
| 857 { |
| 858 return m_timeoutValue; |
| 859 } |
| 860 |
| 861 uint16_t |
| 862 MgtAddBaResponseHeader::GetBufferSize (void) const |
| 863 { |
| 864 return m_bufferSize; |
| 865 } |
| 866 |
| 867 bool |
| 868 MgtAddBaResponseHeader::IsAmsduSupported (void) const |
| 869 { |
| 870 return (m_amsduSupport == 1)?true:false;· |
| 871 } |
| 872 |
| 873 uint16_t |
| 874 MgtAddBaResponseHeader::GetParameterSet (void) const |
| 875 { |
| 876 uint16_t res = 0; |
| 877 res |= m_amsduSupport; |
| 878 res |= m_policy << 1; |
| 879 res |= m_tid << 2; |
| 880 res |= m_bufferSize << 6; |
| 881 return res; |
| 882 } |
| 883 |
| 884 void |
| 885 MgtAddBaResponseHeader::SetParameterSet (uint16_t params) |
| 886 { |
| 887 m_amsduSupport = (params) & 0x01; |
| 888 m_policy = (params >> 1) & 0x01; |
| 889 m_tid = (params >> 2) & 0x0f; |
| 890 m_bufferSize = (params >> 6) & 0x03ff; |
| 891 } |
| 892 |
| 893 /*************************************************** |
| 894 * DelBa |
| 895 ****************************************************/ |
| 896 |
| 897 NS_OBJECT_ENSURE_REGISTERED (MgtDelBaHeader); |
| 898 |
| 899 MgtDelBaHeader::MgtDelBaHeader () |
| 900 : m_reasonCode (1) |
| 901 {} |
| 902 |
| 903 TypeId |
| 904 MgtDelBaHeader::GetTypeId (void) |
| 905 { |
| 906 static TypeId tid = TypeId ("ns3::MgtDelBaHeader") |
| 907 .SetParent<Header> () |
| 908 .AddConstructor<MgtDelBaHeader> () |
| 909 ; |
| 910 return tid; |
| 911 } |
| 912 |
| 913 TypeId |
| 914 MgtDelBaHeader::GetInstanceTypeId (void) const |
| 915 { |
| 916 return GetTypeId (); |
| 917 } |
| 918 |
| 919 void |
| 920 MgtDelBaHeader::Print (std::ostream &os) const |
| 921 {} |
| 922 |
| 923 uint32_t |
| 924 MgtDelBaHeader::GetSerializedSize (void) const |
| 925 { |
| 926 uint32_t size = 0; |
| 927 size += 2; //DelBa parameter set |
| 928 size += 2; //Reason code |
| 929 return size; |
| 930 } |
| 931 |
| 932 void |
| 933 MgtDelBaHeader::Serialize (Buffer::Iterator start) const |
| 934 { |
| 935 Buffer::Iterator i = start; |
| 936 i.WriteHtolsbU16 (GetParameterSet ()); |
| 937 i.WriteHtolsbU16 (m_reasonCode); |
| 938 } |
| 939 |
| 940 uint32_t |
| 941 MgtDelBaHeader::Deserialize (Buffer::Iterator start) |
| 942 { |
| 943 Buffer::Iterator i = start; |
| 944 SetParameterSet (i.ReadLsbtohU16 ()); |
| 945 m_reasonCode = i.ReadLsbtohU16 (); |
| 946 return i.GetDistanceFrom (start); |
| 947 } |
| 948 |
| 949 bool |
| 950 MgtDelBaHeader::IsByOriginator (void) const |
| 951 { |
| 952 return (m_initiator == 1)?true:false; |
| 953 } |
| 954 |
| 955 uint8_t |
| 956 MgtDelBaHeader::GetTid (void) const |
| 957 { |
| 958 NS_ASSERT (m_tid < 16); |
| 959 uint8_t tid = static_cast<uint8_t> (m_tid); |
| 960 return tid; |
| 961 } |
| 962 |
| 963 void |
| 964 MgtDelBaHeader::SetByOriginator (void) |
| 965 { |
| 966 m_initiator = 1; |
| 967 } |
| 968 |
| 969 void |
| 970 MgtDelBaHeader::SetByRecipient (void) |
| 971 { |
| 972 m_initiator = 0; |
| 973 } |
| 974 |
| 975 void |
| 976 MgtDelBaHeader::SetTid (uint8_t tid) |
| 977 { |
| 978 NS_ASSERT (tid < 16); |
| 979 m_tid = static_cast<uint16_t> (tid); |
| 980 } |
| 981 |
| 982 uint16_t |
| 983 MgtDelBaHeader::GetParameterSet (void) const |
| 984 { |
| 985 uint16_t res = 0; |
| 986 res |= m_initiator << 11; |
| 987 res |= m_tid << 12; |
| 988 return res; |
| 989 } |
| 990 |
| 991 void |
| 992 MgtDelBaHeader::SetParameterSet (uint16_t params) |
| 993 { |
| 994 m_initiator = (params >> 11) & 0x01; |
| 995 m_tid = (params >> 12) & 0x0f; |
| 996 } |
| 997 |
531 } // namespace ns3 | 998 } // namespace ns3 |
OLD | NEW |