home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
modula2
/
library
/
queuem2
/
queueadt.mod
< prev
next >
Wrap
Text File
|
1989-08-30
|
12KB
|
370 lines
(* source: h:\modula\code\queues\QueueADT.MOD v1.0a revised: 88.07.22
author: G.Greene, AGCS D/429 (NS/TS), 312/681-7783 created: 88.07.22
function:
This is the implementation code for a package which provides an Abstract
Data Type for queues.
history:
88.07.22 1.0a initial release.
*)
IMPLEMENTATION MODULE QueueADT;
FROM SYSTEM IMPORT (*TYPE*) ADDRESS, BYTE,
(*PROC*) SIZE;
FROM Storage IMPORT (*PROC*) ALLOCATE, DEALLOCATE;
FROM Lib IMPORT (*PROC*) Move;
TYPE
QueueNodePtrs = POINTER TO QueueNodes;
Queues = POINTER TO QueueHeader;
QueueNodes =
RECORD
contents: ADDRESS;
size: CARDINAL;
next: QueueNodePtrs
END;
QueueHeader =
RECORD
front: QueueNodePtrs;
back: QueueNodePtrs
END;
VAR
prevBack: QueueNodePtrs;
(* Create a new (empty) queue specified by the parameter.
NB!! This procedure MUST be invoked for a queue before any other
procedure in this module can be used on that queue. The result of
failing to do so is likely to be disasterous. In addition, this
procedure should not be invoked with an existing queue: the storage
associated with that queue would be lost. To empty an existing queue,
use EmptyTheQueue; to deallocate ("destroy") an existing queue, use
DestroyQueue.
*)
PROCEDURE CreateQueue (
(*out*) VAR queue: Queues );
BEGIN
NEW ( queue );
queue^ .front := NIL;
queue^ .back := NIL;
END CreateQueue;
(* [2]
source: h:\modula\code\queues\QueueADT.MOD v1.0a revised: 88.07.22 *)
(* Empty the queue specified by the parameter of its contents. If the
queue has been destroyed, this procedure will do nothing.
*)
PROCEDURE EmptyTheQueue (
(*in/out*) VAR queue: Queues );
VAR
NodePtr,
NextPtr: QueueNodePtrs;
BEGIN
IF queue # NIL THEN
NodePtr := queue^ .front;
WHILE NodePtr # NIL DO
DEALLOCATE ( NodePtr^ .contents, NodePtr^ .size );
NextPtr := NodePtr^ .next;
DISPOSE ( NodePtr );
NodePtr := NextPtr;
END; (* WHILE *)
END; (* IF queue # NIL *)
END EmptyTheQueue;
(* Destroy the queue specified by the parameter, deallocating all the storage
associated with it. If invoked for a previously destroyed queue, this
procedure will do nothing. A queue destroyed by this procedure will be
ignored by most of the other procedures. The specific action each will
take is indicated in its description. One exception: CreateQueue will
reinstate a queue which has been destroyed.
*)
PROCEDURE DestroyQueue (
(*in/out*) VAR queue: Queues );
BEGIN
IF queue # NIL THEN
EmptyTheQueue ( queue );
DISPOSE ( queue );
queue := NIL;
END;
END DestroyQueue;
(* [3]
source: h:\modula\code\queues\QueueADT.MOD v1.0a revised: 88.07.22 *)
(* Return TRUE if the queue specified by the parameter is currently empty,
or if the queue has been destroyed. Return FALSE only if the queue
exists and has at least one entry.
*)
PROCEDURE isEmpty (
(*in*) queue: Queues ): BOOLEAN;
BEGIN
IF queue = NIL
THEN RETURN TRUE;
ELSE RETURN queue^ .front = NIL;
END;
END isEmpty;
(* Return the number of elements in the queue specified by the parameter.
This procedure will return zero for an empty queue, or for a queue which
has been destroyed.
*)
PROCEDURE QueueSize (
(*in*) queue: Queues ): CARDINAL;
VAR
Node: QueueNodePtrs;
Count: CARDINAL;
BEGIN
Count := 0;
IF queue # NIL THEN
Node := queue^ .front;
WHILE Node # NIL DO
INC ( Count );
Node := Node^ .next;
END;
END; (* IF queue # nil *)
RETURN Count;
END QueueSize;
(* [4]
source: h:\modula\code\queues\QueueADT.MOD v1.0a revised: 88.07.22 *)
(* Enqueue the data specified in the first parameter into the queue specified
by the second parameter in first-in, first-out (FIFO) order. No action
will be taken for a destroyed queue.
*)
PROCEDURE FIFOEnqueue (
(*in*) data: ARRAY OF BYTE;
(*in*) queue: Queues );
VAR
NewNode: QueueNodePtrs;
DataSize: CARDINAL;
BEGIN
IF queue # NIL THEN
NEW ( NewNode );
NewNode^ .next := NIL;
(* determine the size in bytes for the BYTE ARRAY indexed 0..HIGH *)
DataSize := HIGH ( data ) + 1;
NewNode^ .size := DataSize;
ALLOCATE ( NewNode^ .contents, DataSize );
Move ( ADR ( data ), NewNode^ .contents, DataSize );
IF isEmpty ( queue )
THEN queue^ .front := NewNode
ELSE queue^ .back^ .next := NewNode
END;
prevBack := queue^ .back; (* save for use by PriorityEnqueue *)
queue^ .back := NewNode;
END;
END FIFOEnqueue;
(* [5]
source: h:\modula\code\queues\QueueADT.MOD v1.0a revised: 88.07.22 *)
(* Enqueue the data specified in the first parameter into the priority queue
specified by the second parameter. The entry's priority is taken to be
the first word of the data, interpreted as CARDINAL. Zero is the highest
priority; lower priorities have higher CARDINAL values. Thus the queue
yields entries in increasing value of the priority. This ordering was
chosen to accommodate time-based queues. Entries with the same priority
are enqueued in order of receipt (FIFO).
Our implementation strategy is to use FIFOEnqueue to do most of the dirty
work, and then play with the pointers.
*)
PROCEDURE PriorityEnqueue (
(*in*) data: ARRAY OF BYTE;
(*in*) queue: Queues );
VAR
Priority: CARDINAL;
ThisPtr,
LastPtr,
NewNode: QueueNodePtrs;
Scanning: BOOLEAN;
BEGIN
FIFOEnqueue ( data, queue );
(* assemble the priority word value from the first two data bytes *)
Priority := CARDINAL ( data [ 0 ] ) + CARDINAL ( data [ 1 ] ) << 8;
ThisPtr := queue^ .front;
LastPtr := NIL;
Scanning := TRUE;
WHILE Scanning AND ( ThisPtr # NIL ) DO
IF CARDINAL ( ThisPtr^ .contents^ ) > Priority THEN
Scanning := FALSE; (* found a home *)
ELSE (* try the next element *)
LastPtr := ThisPtr;
ThisPtr := ThisPtr^ .next;
END;
END;
(* NB: if ThisPtr = NIL, the entry is already in the proper place at
the end of the queue. *)
IF ThisPtr # NIL THEN
NewNode := queue^ .back; (* save ptr to new node for use *)
NewNode^ .next := ThisPtr;
IF prevBack # NIL THEN (* NewNode is not the only entry ... *)
queue^ .back := prevBack; (* ... there's a next-to-last entry, & *)
prevBack^ .next := NIL; (* ... that entry becomes last again. *)
END;
IF LastPtr = NIL
THEN queue^ .front := NewNode; (* it goes at the front *)
ELSE LastPtr^ .next := NewNode; (* it goes in the middle *)
END; (* IF LastPtr = NIL *)
END; (* IF ThisPtr # NIL *)
END PriorityEnqueue;
(* [6]
source: h:\modula\code\queues\QueueADT.MOD v1.0a revised: 88.07.22 *)
(* Dequeue, to the data area specified by the first parameter, the front
element of the queue specified by the second parameter. If the queue
is empty (or has been destroyed), the third parameter will be set FALSE;
it is also set FALSE if the size of the front data element in the queue
does not match the size of the data area to receive it. Otherwise, the
third parameter is set TRUE, indicating that the first parameter has
valid data. If the third parameter is FALSE, the queue has not been
changed, nor has a value been assigned to the first parameter.
*)
PROCEDURE Dequeue (
(*out*) VAR data: ARRAY OF BYTE;
(*in*) queue: Queues;
(*out*) VAR DataOK: BOOLEAN );
VAR
QueueFront: QueueNodePtrs;
DataSize: CARDINAL;
BEGIN
DataOK := NOT isEmpty ( queue );
IF DataOK THEN
(* determine the size in bytes for the BYTE ARRAY indexed 0..HIGH *)
DataSize := HIGH ( data ) + 1;
QueueFront := queue^ .front;
DataOK := QueueFront^ .size = DataSize;
IF DataOK THEN
Move ( QueueFront^ .contents, ADR ( data ), DataSize );
DEALLOCATE ( QueueFront^ .contents, QueueFront^ .size );
queue^ .front := QueueFront^ .next;
IF queue^ .front = NIL THEN
queue^ .back := NIL
END;
DISPOSE ( QueueFront );
END; (* IF data size match *)
END; (* IF queue not empty *)
END Dequeue;
(* [7]
source: h:\modula\code\queues\QueueADT.MOD v1.0a revised: 88.07.22 *)
(* Copy, to the data area specified by the first parameter, an element of the
queue specified by the second parameter. The element to copy is the one
specified by the third parameter (the front element is numbered zero).
If the queue is empty (or has been destroyed), the fourth parameter will
be set FALSE; it is also set FALSE if the size of the front data element
in the queue does not match the size of the data area to receive it, or
if there is no such element. Otherwise, the fourth parameter is set TRUE,
indicating that the first parameter has valid data. No change is made to
to the queue in any case. This procedure can be used as part of code to
dump the contents of a queue--for example, LOOPing using the value of the
fourth parameter as an EXIT condition.
*)
PROCEDURE ReadElement (
(*out*) VAR data: ARRAY OF BYTE;
(*in*) queue: Queues;
(*in*) element: CARDINAL;
(*out*) VAR DataOK: BOOLEAN );
VAR
QueuePtr: QueueNodePtrs;
DataSize: CARDINAL;
BEGIN
DataOK := NOT isEmpty ( queue );
IF DataOK THEN
QueuePtr := queue^ .front;
WHILE ( QueuePtr # NIL ) AND ( element > 0 ) DO
QueuePtr := QueuePtr^ .next;
DEC ( element );
END;
(* determine the size in bytes for the BYTE ARRAY indexed 0..HIGH *)
DataSize := HIGH ( data ) + 1;
(* NB: the following expression depends on Modula-2 short-circuit
evaluation. care should be used in porting it. *)
DataOK := ( QueuePtr # NIL ) AND ( QueuePtr^ .size = DataSize );
IF DataOK THEN
Move ( QueuePtr^ .contents, ADR ( data ), DataSize );
END; (* IF data size match *)
END; (* IF queue not empty *)
END ReadElement;
END QueueADT.