Фундаментальные алгоритмы и структуры данных в Delphi
Шрифт:
{определить смещение записи и считать ее}
rsSeekStream(rsCalcRecordOffset(aIndex));
rsReadStream(FRecord^, FRecordLen4);
if (PLongint(FRecord)^ = cActiveRecord) then begin
alsDeleted := falser-Move (FRecord^[sizeof(longint)], aRecord, FRecordLen);
end
else begin
alsDeleted := true;
FillChar(aRecord, FRecordLen, 0);
end;
end;
procedure TtdRecordStream.Write(aIndex : longint; var aRecord);
var
DeletedFlag : longint;
begin
{проверить,
if (aIndex < 0) or (aIndex >= Capacity) then
rsError(tdeIndexOutOfBounds, 'Write', aIndex);
{проверить, что запись не была удалена}
rsSeekStream(rsCalcRecordOffset(aIndex));
rsReadStream(DeletedFlag, sizeof(longint));
if (DeletedFlag <> cActiveRecord) then
rsError(tdeRSRecIsDeleted, 'Write', aIndex);
{сохранить запись}
rsWriteStream(aRecord, FRecordLen);
end;
Метод Read возвращает флаг, который показывает, была ли удалена запись. Если запись не удалена, буфер записи, переданный во входном параметре, заполняется записью, считанной из потока. Код просто в один прием считывает всю запись и ее флаг удаления и действует в соответствии со значением флага.
Метод Write, прежде всего, проверяет, была ли удалена требуемая запись. Если запись удалена, она недоступна для изменения, вследствие чего возникает исключение. В противном случае в поток помещается новое значение записи.
И последний метод, связанный с обработкой записей, - это метод Delete.
Листинг 2.24. Чтение и обновление записи в постоянном массиве
procedure TtdRecordStream.Delete(aIndex : longint);
var
DeletedFlag : longint;
begin
{проверить, действителен ли порядковый номер записи}
if (aIndex < 0) or (aIndex >= Capacity) then
rsError(tdeRSOutOfBounds, 'Delete', aIndex);
{проверить, что запись не была удалена}
rsSeekStream(rsCalcRecordOffset(aIndex));
rsReadStream(DeletedFlag, sizeof(longint));
if (DeletedFlag <> cActiveRecord) then
rsError(tdeRSAlreadyDeleted, 'Delete', aIndex);
{записать порядковый номер первой удаленной записи в первые 4 байта удаляемой записи}
rsSeekStream(rsCalcRecordOffset(aIndex));
rsWriteStream(FHeaderRec^.hr1stDelRec, sizeof(longint));
{обновить значение поля первой удаленной записи служебного заголовка, чтобы оно указывало на удаляемую запись}
FHeaderRec^.hr1stDelRec := aIndex;
{количество записей уменьшилось на единицу}
dec(FCount);
dec(FHeaderRec^.hrCount);
{обновить служебный заголовок}
rsSeekStream(FZeroPosition);
rsWriteStream(FHeaderRec^, sizeof(TtdRSHeaderRec));
end;
Метод Delete, прежде всего, проверяет, была ли удалена требуемая запись. Если запись была удалена, метод вызывает ошибку. Если все в порядке, текущее значение поля первой удаленной записи служебного заголовка копируется во флаг удаления записи. Затем значение поля первой удаленной записи устанавливается равным порядковому номеру удаляемой записи, количество активных записей в массиве уменьшается на единицу и обновляется служебный заголовок в потоке.
Метод Clear аналогичен Delete, но он предназначен для удаления всех активных записей постоянного массива.
Листинг 2.25. Очистка содержимого постоянного массива
procedure TtdRecordStream.Clear;
var
Inx : longint;
DeletedFlag : longint;
begin
{выполнить цикл по всем записям и объединить их в одну цепочку удаленных записей}
for Inx := 0 to pred(FCapacity) do
begin
rsSeekStream(rsCalcRecordOffset(Inx));
rsReadStream(DeletedFlag, sizeof(longint));
if (DeletedFlag = cActiveRecord) then begin
{записать порядковый номер первой удаленной записи в первые 4 байта удаляемой записи}
rsSeekStream(rsCalcRecordOffset(Inx));
rsWriteStream(FHeaderRec^.hr1stDelRec, sizeof(longint));
{обновить значение поля первой удаленной записи служебного заголовка, чтобы оно указывало на удаляемую запись}
FHeaderRec^.hr1stDelRec := Inx;
end;
end;
{записей нет}
FCount := 0;
FHeaderRec^.hrCount := 0;
{обновить служебный заголовок}
rsSeekStream(FZeroPosition);
rsWriteStream(FHeaderRec^, sizeof(TtdRSHeaderRec));
end;
Этот метод выполняет цикл по всем записям массива и, если запись активна, удаляет ее в соответствии с алгоритмом, используемым в методе Delete.
Класс TtdRecordStream позволяет также в один прием увеличивать емкость потока на несколько записей, а не добавлять записи по одной с помощью метода Add. Такая возможность позволяет зарезервировать место под постоянный массив, если заранее известно количество записей, которое будет в нем храниться. Запись свойства Capacity осуществляется через метод rsSetCapacity.
Листинг 2.26. Задание емкости постоянного массива
procedure TtdRecordStream.rsSetCapacity(aCapacity : longint);
var
Inx : longint;
begin
{допускается только увеличение емкости}
if (aCapacity > FCapacity) then begin
{заполнить текущую запись нулями}
FillChar(FRecord^, FRecordLen4, 0);
{найти конец файла}
rsSeekStream(rsCalcRecordOffset(FCapacity));
{создать дополнительные записи и внести их в цепочку удаленных записей}