1.\" $NetBSD: queue.3,v 1.7 1997/09/30 16:49:20 christos Exp $ 2.\" 3.\" Copyright (c) 1993 The Regents of the University of California. 4.\" All rights reserved. 5.\" 6.\" Redistribution and use in source and binary forms, with or without 7.\" modification, are permitted provided that the following conditions 8.\" are met: 9.\" 1. Redistributions of source code must retain the above copyright 10.\" notice, this list of conditions and the following disclaimer. 11.\" 2. Redistributions in binary form must reproduce the above copyright 12.\" notice, this list of conditions and the following disclaimer in the 13.\" documentation and/or other materials provided with the distribution. 14.\" 3. All advertising materials mentioning features or use of this software 15.\" must display the following acknowledgement: 16.\" This product includes software developed by the University of 17.\" California, Berkeley and its contributors. 18.\" 4. Neither the name of the University nor the names of its contributors 19.\" may be used to endorse or promote products derived from this software 20.\" without specific prior written permission. 21.\" 22.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32.\" SUCH DAMAGE. 33.\" 34.\" @(#)queue.3 8.1 (Berkeley) 12/13/93 35.\" 36.Dd June 30, 1997 37.Dt QUEUE 3 38.Os BSD 4 39.Sh NAME 40.Nm LIST_ENTRY , 41.Nm LIST_HEAD , 42.Nm LIST_HEAD_INITIALIZER , 43.Nm LIST_INIT , 44.Nm LIST_INSERT_AFTER , 45.Nm LIST_INSERT_BEFORE , 46.Nm LIST_INSERT_HEAD , 47.Nm LIST_REMOVE , 48.Nm SIMPLEQ_ENTRY , 49.Nm SIMPLEQ_HEAD , 50.Nm SIMPLEQ_HEAD_INITIALIZER , 51.Nm SIMPLEQ_INIT , 52.Nm SIMPLEQ_INSERT_HEAD , 53.Nm SIMPLEQ_INSERT_TAIL , 54.Nm SIMPLEQ_INSERT_AFTER , 55.Nm SIMPLEQ_REMOVE_HEAD , 56.Nm TAILQ_ENTRY , 57.Nm TAILQ_HEAD , 58.Nm TAILQ_HEAD_INITIALIZER , 59.Nm TAILQ_INIT , 60.Nm TAILQ_INSERT_AFTER , 61.Nm TAILQ_INSERT_BEFORE , 62.Nm TAILQ_INSERT_HEAD , 63.Nm TAILQ_INSERT_TAIL , 64.Nm TAILQ_REMOVE , 65.Nm CIRCLEQ_ENTRY , 66.Nm CIRCLEQ_HEAD , 67.Nm CIRCLEQ_HEAD_INITIALIZER , 68.Nm CIRCLEQ_INIT , 69.Nm CIRCLEQ_INSERT_AFTER , 70.Nm CIRCLEQ_INSERT_BEFORE , 71.Nm CIRCLEQ_INSERT_HEAD , 72.Nm CIRCLEQ_INSERT_TAIL , 73.Nm CIRCLEQ_REMOVE 74.Nd implementations of lists, tail queues, and circular queues 75.Sh SYNOPSIS 76.Fd #include <sys/queue.h> 77.sp 78.Fn LIST_ENTRY "TYPE" 79.Fn LIST_HEAD "HEADNAME" "TYPE" 80.Fn LIST_HEAD_INITIALIZER "head" 81.Fn LIST_INIT "LIST_HEAD *head" 82.Fn LIST_INSERT_AFTER "TYPE *listelm" "TYPE *elm" "LIST_ENTRY NAME" 83.Fn LIST_INSERT_BEFORE "TYPE *listelm" "TYPE *elm" "LIST_ENTRY NAME" 84.Fn LIST_INSERT_HEAD "LIST_HEAD *head" "TYPE *elm" "LIST_ENTRY NAME" 85.Fn LIST_REMOVE "TYPE *elm" "LIST_ENTRY NAME" 86.sp 87.Fn SIMPLEQ_ENTRY "TYPE" 88.Fn SIMPLEQ_HEAD "HEADNAME" "TYPE" 89.Fn SIMPLEQ_HEAD_INITIALIZER "head" 90.Fn SIMPLEQ_INIT "SIMPLEQ_HEAD *head" 91.Fn SIMPLEQ_INSERT_AFTER "SIMPLEQ_HEAD *head" "TYPE *listelm" "TYPE *elm" "SIMPLEQ_ENTRY NAME" 92.Fn SIMPLEQ_INSERT_HEAD "SIMPLEQ_HEAD *head" "TYPE *elm" "SIMPLEQ_ENTRY NAME" 93.Fn SIMPLEQ_INSERT_TAIL "SIMPLEQ_HEAD *head" "TYPE *elm" "SIMPLEQ_ENTRY NAME" 94.Fn SIMPLEQ_REMOVE_HEAD "SIMPLEQ_HEAD *head" "TYPE *elm" "SIMPLEQ_ENTRY NAME" 95.sp 96.Fn TAILQ_ENTRY "TYPE" 97.Fn TAILQ_HEAD "HEADNAME" "TYPE" 98.Fn TAILQ_HEAD_INITIALIZER "head" 99.Fn TAILQ_INIT "TAILQ_HEAD *head" 100.Fn TAILQ_INSERT_AFTER "TAILQ_HEAD *head" "TYPE *listelm" "TYPE *elm" "TAILQ_ENTRY NAME" 101.Fn TAILQ_INSERT_BEFORE "TYPE *listelm" "TYPE *elm" "TAILQ_ENTRY NAME" 102.Fn TAILQ_INSERT_HEAD "TAILQ_HEAD *head" "TYPE *elm" "TAILQ_ENTRY NAME" 103.Fn TAILQ_INSERT_TAIL "TAILQ_HEAD *head" "TYPE *elm" "TAILQ_ENTRY NAME" 104.Fn TAILQ_REMOVE "TAILQ_HEAD *head" "TYPE *elm" "TAILQ_ENTRY NAME" 105.sp 106.Fn CIRCLEQ_ENTRY "TYPE" 107.Fn CIRCLEQ_HEAD "HEADNAME" "TYPE" 108.Fn CIRCLEQ_HEAD_INITIALIZER "head" 109.Fn CIRCLEQ_INIT "CIRCLEQ_HEAD *head" 110.Fn CIRCLEQ_INSERT_AFTER "CIRCLEQ_HEAD *head" "TYPE *listelm" "TYPE *elm" "CIRCLEQ_ENTRY NAME" 111.Fn CIRCLEQ_INSERT_BEFORE "CIRCLEQ_HEAD *head" "TYPE *listelm" "TYPE *elm" "CIRCLEQ_ENTRY NAME" 112.Fn CIRCLEQ_INSERT_HEAD "CIRCLEQ_HEAD *head" "TYPE *elm" "CIRCLEQ_ENTRY NAME" 113.Fn CIRCLEQ_INSERT_TAIL "CIRCLEQ_HEAD *head" "TYPE *elm" "CIRCLEQ_ENTRY NAME" 114.Fn CIRCLEQ_REMOVE "CIRCLEQ_HEAD *head" "TYPE *elm" "CIRCLEQ_ENTRY NAME" 115.Sh DESCRIPTION 116These macros define and operate on four types of data structures: 117lists, simple queues, tail queues, and circular queues. 118All four structures support the following functionality: 119.Bl -enum -compact -offset indent 120.It 121Insertion of a new entry at the head of the list. 122.It 123Insertion of a new entry before or after any element in the list. 124.It 125Removal of any entry in the list. 126.It 127Forward traversal through the list. 128.El 129.Pp 130Lists are the simplest of the four data structures and support 131only the above functionality. 132.Pp 133Simple queues add the following functionality: 134.Bl -enum -compact -offset indent 135.It 136Entries can be added at the end of a list. 137.El 138However: 139.Bl -enum -compact -offset indent 140.It 141Entries may not be added before any element in the list. 142.It 143Only the first entry in the list may be removed. 144.It 145All list insertions and removals must specify the head of the list. 146.It 147Each head entry requires two pointers rather than one. 148.El 149.Pp 150Tail queues add the following functionality: 151.Bl -enum -compact -offset indent 152.It 153Entries can be added at the end of a list. 154.El 155However: 156.Bl -enum -compact -offset indent 157.It 158All list insertions and removals, except insertion before another element, must 159specify the head of the list. 160.It 161Each head entry requires two pointers rather than one. 162.It 163Code size is about 15% greater and operations run about 20% slower 164than lists. 165.El 166.Pp 167Circular queues add the following functionality: 168.Bl -enum -compact -offset indent 169.It 170Entries can be added at the end of a list. 171.It 172They may be traversed backwards, from tail to head. 173.El 174However: 175.Bl -enum -compact -offset indent 176.It 177All list insertions and removals must specify the head of the list. 178.It 179Each head entry requires two pointers rather than one. 180.It 181The termination condition for traversal is more complex. 182.It 183Code size is about 40% greater and operations run about 45% slower 184than lists. 185.El 186.Pp 187In the macro definitions, 188.Fa TYPE 189is the name of a user defined structure, 190that must contain a field of type 191.Li LIST_ENTRY , 192.Li SIMPLEQ_ENTRY , 193.Li TAILQ_ENTRY , 194or 195.Li CIRCLEQ_ENTRY , 196named 197.Fa NAME . 198The argument 199.Fa HEADNAME 200is the name of a user defined structure that must be declared 201using the macros 202.Li LIST_HEAD , 203.Li SIMPLEQ_HEAD , 204.Li TAILQ_HEAD , 205or 206.Li CIRCLEQ_HEAD . 207See the examples below for further explanation of how these 208macros are used. 209.Sh LISTS 210A list is headed by a structure defined by the 211.Nm LIST_HEAD 212macro. 213This structure contains a single pointer to the first element 214on the list. 215The elements are doubly linked so that an arbitrary element can be 216removed without traversing the list. 217New elements can be added to the list after an existing element, 218before an existing element, or at the head of the list. 219A 220.Fa LIST_HEAD 221structure is declared as follows: 222.Bd -literal -offset indent 223LIST_HEAD(HEADNAME, TYPE) head; 224.Ed 225.sp 226where 227.Fa HEADNAME 228is the name of the structure to be defined, and 229.Fa TYPE 230is the type of the elements to be linked into the list. 231A pointer to the head of the list can later be declared as: 232.Bd -literal -offset indent 233struct HEADNAME *headp; 234.Ed 235.sp 236(The names 237.Li head 238and 239.Li headp 240are user selectable.) 241.Pp 242The macro 243.Nm LIST_ENTRY 244declares a structure that connects the elements in 245the list. 246.Pp 247The macro 248.Nm LIST_HEAD_INITIALIZER 249provides a value which can be used to initialize a list head at 250compile time, and is used at the point that the list head 251variable is declared, like: 252.Bd -literal -offset indent 253struct HEADNAME head = LIST_HEAD_INITIALIZER(head); 254.Ed 255.Pp 256The macro 257.Nm LIST_INIT 258initializes the list referenced by 259.Fa head . 260.Pp 261The macro 262.Nm LIST_INSERT_HEAD 263inserts the new element 264.Fa elm 265at the head of the list. 266.Pp 267The macro 268.Nm LIST_INSERT_AFTER 269inserts the new element 270.Fa elm 271after the element 272.Fa listelm . 273.Pp 274The macro 275.Nm LIST_INSERT_BEFORE 276inserts the new element 277.Fa elm 278before the element 279.Fa listelm . 280.Pp 281The macro 282.Nm LIST_REMOVE 283removes the element 284.Fa elm 285from the list. 286.Sh LIST EXAMPLE 287.Bd -literal 288LIST_HEAD(listhead, entry) head; 289struct listhead *headp; /* List head. */ 290struct entry { 291 ... 292 LIST_ENTRY(entry) entries; /* List. */ 293 ... 294} *n1, *n2, *np; 295 296LIST_INIT(&head); /* Initialize the list. */ 297 298n1 = malloc(sizeof(struct entry)); /* Insert at the head. */ 299LIST_INSERT_HEAD(&head, n1, entries); 300 301n2 = malloc(sizeof(struct entry)); /* Insert after. */ 302LIST_INSERT_AFTER(n1, n2, entries); 303 304n2 = malloc(sizeof(struct entry)); /* Insert before. */ 305LIST_INSERT_BEFORE(n1, n2, entries); 306 /* Forward traversal. */ 307for (np = head.lh_first; np != NULL; np = np->entries.le_next) 308 np-> ... 309 310while (head.lh_first != NULL) /* Delete. */ 311 LIST_REMOVE(head.lh_first, entries); 312.Ed 313.Sh SIMPLE QUEUES 314A simple queue is headed by a structure defined by the 315.Nm SIMPLEQ_HEAD 316macro. 317This structure contains a pair of pointers, 318one to the first element in the simple queue and the other to 319the last element in the simple queue. 320The elements are doubly linked so that an arbitrary element can be 321removed without traversing the simple queue. 322New elements can be added to the queue after an existing element, 323before an existing element, at the head of the queue, or at the end 324the queue. 325A 326.Fa SIMPLEQ_HEAD 327structure is declared as follows: 328.Bd -literal -offset indent 329SIMPLEQ_HEAD(HEADNAME, TYPE) head; 330.Ed 331.sp 332where 333.Li HEADNAME 334is the name of the structure to be defined, and 335.Li TYPE 336is the type of the elements to be linked into the simple queue. 337A pointer to the head of the simple queue can later be declared as: 338.Bd -literal -offset indent 339struct HEADNAME *headp; 340.Ed 341.sp 342(The names 343.Li head 344and 345.Li headp 346are user selectable.) 347.Pp 348The macro 349.Nm SIMPLEQ_ENTRY 350declares a structure that connects the elements in 351the simple queue. 352.Pp 353The macro 354.Nm SIMPLEQ_HEAD_INITIALIZER 355provides a value which can be used to initialize a simple queue head at 356compile time, and is used at the point that the simple queue head 357variable is declared, like: 358.Bd -literal -offset indent 359struct HEADNAME head = SIMPLEQ_HEAD_INITIALIZER(head); 360.Ed 361.Pp 362The macro 363.Nm SIMPLEQ_INIT 364initializes the simple queue referenced by 365.Fa head . 366.Pp 367The macro 368.Nm SIMPLEQ_INSERT_HEAD 369inserts the new element 370.Fa elm 371at the head of the simple queue. 372.Pp 373The macro 374.Nm SIMPLEQ_INSERT_TAIL 375inserts the new element 376.Fa elm 377at the end of the simple queue. 378.Pp 379The macro 380.Nm SIMPLEQ_INSERT_AFTER 381inserts the new element 382.Fa elm 383after the element 384.Fa listelm . 385.Pp 386The macro 387.Nm SIMPLEQ_REMOVE_HEAD 388removes the first element from the simple queue. 389.Sh SIMPLE QUEUE EXAMPLE 390.Bd -literal 391SIMPLEQ_HEAD(simplehead, entry) head; 392struct simplehead *headp; /* Simple queue head. */ 393struct entry { 394 ... 395 SIMPLEQ_ENTRY(entry) entries; /* Simple queue. */ 396 ... 397} *n1, *n2, *np; 398 399SIMPLEQ_INIT(&head); /* Initialize the queue. */ 400 401n1 = malloc(sizeof(struct entry)); /* Insert at the head. */ 402SIMPLEQ_INSERT_HEAD(&head, n1, entries); 403 404n1 = malloc(sizeof(struct entry)); /* Insert at the tail. */ 405SIMPLEQ_INSERT_TAIL(&head, n1, entries); 406 407n2 = malloc(sizeof(struct entry)); /* Insert after. */ 408SIMPLEQ_INSERT_AFTER(&head, n1, n2, entries); 409 410 /* Forward traversal. */ 411for (np = head.sqh_first; np != NULL; np = np->entries.sqe_next) 412 np-> ... 413 /* Delete. */ 414while (head.sqh_first != NULL) 415 SIMPLEQ_REMOVE_HEAD(&head, head.sqh_first, entries); 416.Ed 417.Sh TAIL QUEUES 418A tail queue is headed by a structure defined by the 419.Nm TAILQ_HEAD 420macro. 421This structure contains a pair of pointers, 422one to the first element in the tail queue and the other to 423the last element in the tail queue. 424The elements are doubly linked so that an arbitrary element can be 425removed without traversing the tail queue. 426New elements can be added to the queue after an existing element, 427before an existing element, at the head of the queue, or at the end 428the queue. 429A 430.Fa TAILQ_HEAD 431structure is declared as follows: 432.Bd -literal -offset indent 433TAILQ_HEAD(HEADNAME, TYPE) head; 434.Ed 435.sp 436where 437.Li HEADNAME 438is the name of the structure to be defined, and 439.Li TYPE 440is the type of the elements to be linked into the tail queue. 441A pointer to the head of the tail queue can later be declared as: 442.Bd -literal -offset indent 443struct HEADNAME *headp; 444.Ed 445.sp 446(The names 447.Li head 448and 449.Li headp 450are user selectable.) 451.Pp 452The macro 453.Nm TAILQ_ENTRY 454declares a structure that connects the elements in 455the tail queue. 456.Pp 457The macro 458.Nm TAILQ_HEAD_INITIALIZER 459provides a value which can be used to initialize a tail queue head at 460compile time, and is used at the point that the tail queue head 461variable is declared, like: 462.Bd -literal -offset indent 463struct HEADNAME head = TAILQ_HEAD_INITIALIZER(head); 464.Ed 465.Pp 466The macro 467.Nm TAILQ_INIT 468initializes the tail queue referenced by 469.Fa head . 470.Pp 471The macro 472.Nm TAILQ_INSERT_HEAD 473inserts the new element 474.Fa elm 475at the head of the tail queue. 476.Pp 477The macro 478.Nm TAILQ_INSERT_TAIL 479inserts the new element 480.Fa elm 481at the end of the tail queue. 482.Pp 483The macro 484.Nm TAILQ_INSERT_AFTER 485inserts the new element 486.Fa elm 487after the element 488.Fa listelm . 489.Pp 490The macro 491.Nm TAILQ_INSERT_BEFORE 492inserts the new element 493.Fa elm 494before the element 495.Fa listelm . 496.Pp 497The macro 498.Nm TAILQ_REMOVE 499removes the element 500.Fa elm 501from the tail queue. 502.Sh TAIL QUEUE EXAMPLE 503.Bd -literal 504TAILQ_HEAD(tailhead, entry) head; 505struct tailhead *headp; /* Tail queue head. */ 506struct entry { 507 ... 508 TAILQ_ENTRY(entry) entries; /* Tail queue. */ 509 ... 510} *n1, *n2, *np; 511 512TAILQ_INIT(&head); /* Initialize the queue. */ 513 514n1 = malloc(sizeof(struct entry)); /* Insert at the head. */ 515TAILQ_INSERT_HEAD(&head, n1, entries); 516 517n1 = malloc(sizeof(struct entry)); /* Insert at the tail. */ 518TAILQ_INSERT_TAIL(&head, n1, entries); 519 520n2 = malloc(sizeof(struct entry)); /* Insert after. */ 521TAILQ_INSERT_AFTER(&head, n1, n2, entries); 522 523n2 = malloc(sizeof(struct entry)); /* Insert before. */ 524TAILQ_INSERT_BEFORE(n1, n2, entries); 525 /* Forward traversal. */ 526for (np = head.tqh_first; np != NULL; np = np->entries.tqe_next) 527 np-> ... 528 /* Delete. */ 529while (head.tqh_first != NULL) 530 TAILQ_REMOVE(&head, head.tqh_first, entries); 531.Ed 532.Sh CIRCULAR QUEUES 533A circular queue is headed by a structure defined by the 534.Nm CIRCLEQ_HEAD 535macro. 536This structure contains a pair of pointers, 537one to the first element in the circular queue and the other to the 538last element in the circular queue. 539The elements are doubly linked so that an arbitrary element can be 540removed without traversing the queue. 541New elements can be added to the queue after an existing element, 542before an existing element, at the head of the queue, or at the end 543of the queue. 544A 545.Fa CIRCLEQ_HEAD 546structure is declared as follows: 547.Bd -literal -offset indent 548CIRCLEQ_HEAD(HEADNAME, TYPE) head; 549.Ed 550.sp 551where 552.Li HEADNAME 553is the name of the structure to be defined, and 554.Li TYPE 555is the type of the elements to be linked into the circular queue. 556A pointer to the head of the circular queue can later be declared as: 557.Bd -literal -offset indent 558struct HEADNAME *headp; 559.Ed 560.sp 561(The names 562.Li head 563and 564.Li headp 565are user selectable.) 566.Pp 567The macro 568.Nm CIRCLEQ_ENTRY 569declares a structure that connects the elements in 570the circular queue. 571.Pp 572The macro 573.Nm CIRCLEQ_HEAD_INITIALIZER 574provides a value which can be used to initialize a circular queue head at 575compile time, and is used at the point that the circular queue head 576variable is declared, like: 577.Bd -literal -offset indent 578struct HEADNAME head = CIRCLEQ_HEAD_INITIALIZER(head); 579.Ed 580.Pp 581The macro 582.Nm CIRCLEQ_INIT 583initializes the circular queue referenced by 584.Fa head . 585.Pp 586The macro 587.Nm CIRCLEQ_INSERT_HEAD 588inserts the new element 589.Fa elm 590at the head of the circular queue. 591.Pp 592The macro 593.Nm CIRCLEQ_INSERT_TAIL 594inserts the new element 595.Fa elm 596at the end of the circular queue. 597.Pp 598The macro 599.Nm CIRCLEQ_INSERT_AFTER 600inserts the new element 601.Fa elm 602after the element 603.Fa listelm . 604.Pp 605The macro 606.Nm CIRCLEQ_INSERT_BEFORE 607inserts the new element 608.Fa elm 609before the element 610.Fa listelm . 611.Pp 612The macro 613.Nm CIRCLEQ_REMOVE 614removes the element 615.Fa elm 616from the circular queue. 617.Sh CIRCULAR QUEUE EXAMPLE 618.Bd -literal 619CIRCLEQ_HEAD(circleq, entry) head; 620struct circleq *headp; /* Circular queue head. */ 621struct entry { 622 ... 623 CIRCLEQ_ENTRY entries; /* Circular queue. */ 624 ... 625} *n1, *n2, *np; 626 627CIRCLEQ_INIT(&head); /* Initialize the circular queue. */ 628 629n1 = malloc(sizeof(struct entry)); /* Insert at the head. */ 630CIRCLEQ_INSERT_HEAD(&head, n1, entries); 631 632n1 = malloc(sizeof(struct entry)); /* Insert at the tail. */ 633CIRCLEQ_INSERT_TAIL(&head, n1, entries); 634 635n2 = malloc(sizeof(struct entry)); /* Insert after. */ 636CIRCLEQ_INSERT_AFTER(&head, n1, n2, entries); 637 638n2 = malloc(sizeof(struct entry)); /* Insert before. */ 639CIRCLEQ_INSERT_BEFORE(&head, n1, n2, entries); 640 /* Forward traversal. */ 641for (np = head.cqh_first; np != (void *)&head; np = np->entries.cqe_next) 642 np-> ... 643 /* Reverse traversal. */ 644for (np = head.cqh_last; np != (void *)&head; np = np->entries.cqe_prev) 645 np-> ... 646 /* Delete. */ 647while (head.cqh_first != (void *)&head) 648 CIRCLEQ_REMOVE(&head, head.cqh_first, entries); 649.Ed 650.Sh HISTORY 651The 652.Nm queue 653functions first appeared in 4.4BSD. 654The 655.Nm SIMPLEQ 656functions first appeared in 657.Nx 1.2 . 658