Чтение онлайн

ЖАНРЫ

Фундаментальные алгоритмы и структуры данных в Delphi
Шрифт:

Метод rsCalcRecordOffset просто вычисляет смещение записи, порядковый номер которой передан ему во входном параметре. При этом учитывается начальное положение потока и размер служебного заголовка.

Листинг 2.22. Добавление новой записи в постоянный массив

function TtdRecordStream.Add(var aRecord): longint;

begin

{если цепочка удаленных записей пуста, в поток добавляется новая запись}

if (FHeaderRec^.hr1stDelRec = cEndOfDeletedChain) then begin

Result :=FCapacity;

inc(FCapacity);

inc(FHeaderRec^.hrCapacity);

end

противном случае используется первая удаленная запись, обновляется значение поля удаленной записи в служебном заголовке для указания на следующую удаленную запись}

else begin

Result := FHeaderRec^.hr1stDelRec;

rsSeekStream(rsCalcRecordOffset(FHeaderRec^.hr1stDelRec))/ rsReadStream(FHeaderRec^.hr1stDelRec, sizeof(longint));

end;

{определить смещение записи и сохранить новую запись}

rsSeekStream(rsCalcRecordOffset(Result));

PLongint(FRecord)^ := cActiveRecord;

Move(aRecord, FRecord^[sizeof(longint)], FRecordLen);

rsWritestream(FRecord^, FRecordLen4);

{количество записей увеличилось на единицу}

inc(FCount);

inc(FHeaderRec^.hrCount);

{обновить служебный заголовок}

rsSeekStream(FZeroPosition);

rsWriteStream(FHeaderRec^, sizeof(TtdRSHeaderRec));

end;

Если цепочка удаленных записей не пуста, определить первую удаленную запись (именно поверх нее будет сохраняться новая запись). Мы считываем флаг удаления для этой записи и обновляем поле первой удаленной записи служебного заголовка. Затем мы определяем положение начала удаленной записи, устанавливаем значение флага удаления равным cActiveRecord (-1) и сохраняем запись, переданную методу во входном параметре.

При считывании и сохранении записей необходимо учитывать, удалена ли требуемая запись. Записи идентифицируются по их порядковым номерам.

Листинг 2.23. Чтение и обновление записи в постоянном массиве

procedure TtdRecordStream.Read(aIndex : longint; var aRecord; var alsDeleted : boolean);

begin

{проверить, действителен ли порядковый номер записи}

if (aIndex < 0) or (aIndex >= Capacity) then

rsError(tdeRSOutOfBounds, 'Read', aIndex);

{определить смещение записи и считать ее}

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));

Поделиться с друзьями: