25.11.2014 Views

Algorithms and Data Structures

Algorithms and Data Structures

Algorithms and Data Structures

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

N.Wirth. <strong>Algorithms</strong> <strong>and</strong> <strong>Data</strong> <strong>Structures</strong>. Oberon version 29<br />

relatively large blocks once the tape is moving. Similar conditions hold for magnetic disks, where the data<br />

are allocated on tracks with a fixed number of blocks of fixed size, the so-called block size. In fact, a disk<br />

should be regarded as an array of blocks, each block being read or written as a whole, containing typically<br />

2 k bytes with k = 8, 9, … 12.<br />

Our programs, however, do not observe any such timing constraints. In order to allow them to ignore<br />

the constraints, the data to be transferred are buffered. They are collected in a buffer variable (in main<br />

store) <strong>and</strong> transferred when a sufficient amount of data is accumulated to form a block of the required size.<br />

The buffer's client has access only via the two procedures deposit <strong>and</strong> fetch:<br />

DEFINITION Buffer;<br />

PROCEDURE deposit (x: CHAR);<br />

PROCEDURE fetch (VAR x: CHAR);<br />

END Buffer.<br />

Buffering has an additional advantage in allowing the process which generates (receives) data to proceed<br />

concurrently with the device that writes (reads) the data from (to) the buffer. In fact, it is convenient to<br />

regard the device as a process itself which merely copies data streams. The buffer's purpose is to provide a<br />

certain degree of decoupling between the two processes, which we shall call the producer <strong>and</strong> the<br />

consumer. If, for example, the consumer is slow at a certain moment, it may catch up with the producer<br />

later on. This decoupling is often essential for a good utilization of peripheral devices, but it has only an<br />

effect, if the rates of producer <strong>and</strong> consumer are about the same on the average, but fluctuate at times. The<br />

degree of decoupling grows with increasing buffer size.<br />

We now turn to the question of how to represent a buffer, <strong>and</strong> shall for the time being assume that data<br />

elements are deposited <strong>and</strong> fetched individually instead of in blocks. A buffer essentially constitutes a firstin-first-out<br />

queue (fifo). If it is declared as an array, two index variables, say in <strong>and</strong> out, mark the positions<br />

of the next location to be written into <strong>and</strong> to be read from. Ideally, such an array should have no index<br />

bounds. A finite array is quite adequate, however, considering the fact that elements once fetched are no<br />

longer relevant. Their location may well be re-used. This leads to the idea of the circular buffer.<br />

in<br />

out<br />

Fig. 1.8. Circular buffer with indices in <strong>and</strong> out.<br />

The operations of depositing <strong>and</strong> fetching an element are expressed in the following module, which<br />

exports these operations as procedures, but hides the buffer <strong>and</strong> its index variables — <strong>and</strong> thereby<br />

effectively the buffering mechanism — from the client processes. This mechanism also involves a variable n<br />

counting the number of elements currently in the buffer. If N denotes the size of the buffer, the condition<br />

0 ≤ n ≤ N. Therefore, the operation fetch must be guarded by the condition n > 0 (buffer non-empty), <strong>and</strong><br />

the operation deposit by the condition n < N (buffer non-full). Not meeting the former condition must be<br />

regarded as a programming error, a violation of the latter as a failure of the suggested implementation<br />

(buffer too small).

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!