pascalabcnet/TestSuite/CompilationSamples/BlockFileOfT.pas

844 lines
32 KiB
ObjectPascal
Raw Normal View History

2019-06-21 09:36:54 +03:00
//*****************************************************************************************************\\
2018-11-08 00:24:28 +03:00
// Copyright (©) Cergey Latchenko ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega )
// This code is distributed under the Unlicense
2019-06-21 09:36:54 +03:00
// For details see LICENSE.md file or this:
2018-11-08 00:24:28 +03:00
// https://github.com/SunSerega/PascalABC.Net-BlockFileOfT/blob/master/LICENSE.md
2019-06-21 09:36:54 +03:00
//*****************************************************************************************************\\
// Copyright (©) Сергей Латченко ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega )
2018-11-08 00:24:28 +03:00
// Этот код распространяется под Unlicense
2019-06-21 09:36:54 +03:00
// Для деталей смотрите в файл LICENSE.md или это:
2018-11-08 00:24:28 +03:00
// https://github.com/SunSerega/PascalABC.Net-BlockFileOfT/blob/master/LICENSE.md
2019-06-21 09:36:54 +03:00
//*****************************************************************************************************\\
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
///Модуль, содержащий тип BlockFileOf<T>
///Тип-альтернатива стандартному file of T
///Главное преимущество - скорость работы
2018-11-08 00:24:28 +03:00
unit BlockFileOfT;
interface
uses System.Runtime.InteropServices;
uses System.IO;
type
///--
BlockFileBase = abstract class
2019-01-26 11:43:27 +03:00
protected fi: FileInfo;
protected _offset: int64;
protected str: FileStream;
protected bw: BinaryWriter;
protected br: BinaryReader;
2018-11-08 00:24:28 +03:00
protected linked := new List<BlockFileBase>;
2019-01-26 11:43:27 +03:00
protected procedure Link(f: BlockFileBase);
2018-11-08 00:24:28 +03:00
protected procedure UnLink;
2019-01-26 11:43:27 +03:00
private static function MessageBox(wnd: System.IntPtr; message, caption: string; flags: cardinal): integer; external 'User32.dll';
2018-11-08 00:24:28 +03:00
end;
2019-01-26 11:43:27 +03:00
///Тип, записывающий данные в файл по схожему с "file of <T>" принципу
///В отличие от типизированных файлов данный тип сохраняет всю запись одним блоком так как она записана в памяти.
2019-01-26 11:43:27 +03:00
///Это даёт значительное преимущество по скорости, но ограничивает типы, которые могут быть использованы в качестве полей типа <T>
2018-11-08 00:24:28 +03:00
///
///В частности, поля записи T и всех вложенных записей не могут быть:
2019-01-26 11:43:27 +03:00
/// - Указателями
/// - Ссылочными типами (классами, динамическими массивами)
/// - Типами char и System.DateTime
///Эти ограничения можно обойти, об чем сказано в справке
2019-01-26 11:43:27 +03:00
BlockFileOf<T> = class(BlockFileBase)
private static sz: integer;
private static procedure TestForRefT(tt: System.Type);
2018-11-08 00:24:28 +03:00
begin
if tt.IsClass then
begin
MessageBox(new System.IntPtr(nil),
$'Тип {tt} ссылочный.{#10}Ссылочные типы нельзя сохранять в типизированный файл.{#10}Нажмите OK для выхода.',
$'Тип T из BlockFileOf<T> содержет ссылочные типы',
$10
);
Halt(-1);
end;
foreach var fi in
tt.GetFields(
System.Reflection.BindingFlags.GetField or
System.Reflection.BindingFlags.Instance or
System.Reflection.BindingFlags.Public or
System.Reflection.BindingFlags.NonPublic
) do
if not fi.IsLiteral then
if fi.FieldType <> tt then//integer имеет поле типа integer, без этой строчки StackOwerflow
TestForRefT(fi.FieldType);
end;
2019-01-26 11:43:27 +03:00
private static constructor :=
2018-11-08 00:24:28 +03:00
try
TestForRefT(typeof(T));
(*
try
var a := new T[0];
GCHandle.Alloc(a,GCHandleType.Pinned).Free;
except
on e: System.ArgumentException do
begin
MessageBox(new System.IntPtr(nil),
$'.Net не принимает какой то из типов полей записи, для которой создан BlockFileOf<T>{#10}Нажмите OK для выхода.',
$'Тип T содержит ссылочные типы',
2018-11-08 00:24:28 +03:00
$10
);
Halt(-1);
end;
end;
(**)
sz := Marshal.SizeOf(typeof(T));
except
on e:Exception do
begin
MessageBox(new System.IntPtr(nil),
e.ToString,
$'При инициализации BlockFileOf<{typeof(T)}> произошла ошибка',
$10
);
Halt(-1);
end;
end;
2019-01-26 11:43:27 +03:00
private function GetName: string;
private function GetFullName: string;
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
private function GetExists: boolean;
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
private function GetFileSize: int64 := (GetByteFileSize - _offset) div sz;
private procedure SetFileSize(size: int64) := SetByteFileSize(_offset + size*sz);
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
private function GetByteFileSize: int64;
private procedure SetByteFileSize(size: int64);
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
private function GetPos: int64 := (GetPosByte-_offset) div sz;
private procedure SetPos(pos: int64) := SetPosByte(_offset + pos*sz);
private function GetPosByte: int64;
private procedure SetPosByte(pos: int64);
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
private function InternalReadLazy(c: integer; start_pos: int64):sequence of T;
2018-11-08 00:24:28 +03:00
///Инициализирует переменную файла, не привязывая её к файлу на диске
public constructor := exit;
2019-01-26 11:43:27 +03:00
///Инициализирует переменную файла, привязывая её к файлу с именем fname
public constructor(fname: string) :=
2018-11-08 00:24:28 +03:00
Assign(fname);
2019-01-26 11:43:27 +03:00
///Инициализирует переменную файла, привязывая её к файлу с именем fname
///Устанавливает значение смещения от начала в байтах на offset
public constructor(fname: string; offset: int64) :=
2018-11-08 00:24:28 +03:00
Assign(fname, offset);
2019-01-26 11:43:27 +03:00
2018-11-08 00:24:28 +03:00
///- constructor BlockFileOf<>(f:BlockFileOf<>);
2019-01-26 11:43:27 +03:00
///Инициализирует новую переменную, создавая связку с заданной переменной
///После вызова этого конструктора переданная и созданная переменные будут использовать общий файловый поток, но записывать разные типы данных (у них может быть разный T)
///Это значит, что переменная, которую передали в конструктор, уже должна иметь открытый файловый поток
2018-11-08 00:24:28 +03:00
///Метод Close разрывает эту связь.
public constructor(f: BlockFileBase) :=
Link(f);
2019-01-26 11:43:27 +03:00
///Возвращает или задаёт размер блока из одного элемента в байтах
///Задавать это свойство не рекомендуется
public property TSize: integer read integer(sz) write sz := value;
///Возвращает или задаёт смещение от начала файла до начала элементов в байтах
public property Offset: int64 read _offset write _offset;
///Возвращает или задаёт количество сохранённых в файл элементов
///Задавать можно только после открытия файла
public property Size: int64 read GetFileSize write SetFileSize;
///Возвращает или задаёт размер файла в байтах
///Задавать можно только после открытия файла
public property ByteSize: int64 read GetByteFileSize write SetByteFileSize;
///Возвращает неполное имя файла
public property Name: string read GetName;
///Возвращает полное имя файла
public property FullName: string read GetFullName;
///Определяет, существует ли файл
public property Exists: boolean read GetExists;
///Возвращает номер текущего элемета в файле (нумеруя с 0)
public property Pos: int64 read GetPos write SetPos;
///Возвращает номер текущего байта от начала файла (нумеруя с 0)
public property PosByte: int64 read GetPosByte write SetPosByte;
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
///Определяет, привязана ли переменная к файлу
public property Assigned: boolean read fi <> nil;
///Определяет, открыт ли файл
public property Opened: boolean read str <> nil;
///Определяет, достигнут ли конец файла
public property EOF: boolean read ByteSize-PosByte < sz;
///Возвращает поток текущего файла (или nil если файл не открыт)
///Внимание! Любое действие, связанное с изменением данного потока файла, приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете
2019-01-26 11:43:27 +03:00
public property BaseStream: FileStream read str;
///Возвращает BinaryWriter текущего файла (или nil если файл не открыт)
///Внимание! Любое действие, связанное с изменением основного потока файла приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете
2019-01-26 11:43:27 +03:00
public property BinWriter: BinaryWriter read bw;
///Возвращает BinaryReader текущего файла (или nil если файл не открыт)
///Внимание! Любое действие, связанное с изменением основного потока файла приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете
2019-01-26 11:43:27 +03:00
public property BinReader: BinaryReader read br;
///Возвращает FileInfo текущего файла (или nil если переменная не привязана к файлу)
2019-01-26 11:43:27 +03:00
public property FileInfo: System.IO.FileInfo read fi;
///Привязывает текущий экземпляр BlockFileOf<T> к файлу с именем fname
///Привязывать можно и к несуществующим файлам, при открытии на запись будет создан новый файл
public procedure Assign(fname: string);
///Привязывает данную переменную к файлу с именем fname
///Устанавливает смещение от начала файла до начала элементов в байтах на offset
///Привязывать можно и к несуществующим файлам, при открытии с использованием Rewrite будет создан новый файл
2019-01-26 11:43:27 +03:00
public procedure Assign(fname: string; offset: int64);
///Удаляет связь переменной с файлом, если связь есть
2018-11-08 00:24:28 +03:00
public procedure UnAssign;
2019-01-26 11:43:27 +03:00
///Открывает файл способом mode
public procedure Open(mode: FileMode);
2018-11-08 00:24:28 +03:00
///Удаляет связаный файл, если он существует
public procedure Delete;
2019-01-26 11:43:27 +03:00
2018-11-08 00:24:28 +03:00
///Переименовывает файл
2019-01-26 11:43:27 +03:00
///При указании иного расположения файл будет перемещён
public procedure Rename(NewName: string);
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
///Создает файл
2018-11-08 00:24:28 +03:00
public procedure Rewrite;
2019-01-26 11:43:27 +03:00
///Привязывает данную переменную к файлу с именем fname и создает (или обнуляет) его
public procedure Rewrite(fname: string);
///Привязывает данную переменную к файлу с именем fname и создает (или обнуляет) его
///Устанавливает смещение от начала файла до начала элементов в байтах на offset
public procedure Rewrite(fname: string; offset: int64);
///Открывает файл на чтение
2018-11-08 00:24:28 +03:00
public procedure Reset;
2019-01-26 11:43:27 +03:00
///Привязывает данную переменную к файлу с именем fname и открывает его на чтение
public procedure Reset(fname: string);
///Привязывает данную переменную к файлу с именем fname и открывает его на чтение
///Устанавливает смещение от начала файла до начала элементов в байтах на offset
public procedure Reset(fname: string; offset: int64);
///Открывает файл и устанавливает позицию на конец файла
2018-11-08 00:24:28 +03:00
public procedure Append;
2019-01-26 11:43:27 +03:00
///Привязывает данную переменную к файлу с именем fname и открывает его, устанавливая позицию на конец файла
public procedure Append(fname: string);
///Привязывает данную переменную к файлу с именем fname и открывает его, устанавливая позицию на конец файла
///Устанавливает смещение от начала файла до начала элементов в байтах на offset
public procedure Append(fname: string; offset: int64);
///Ставит позицию в файле на элемент pos (нумеруя с 0)
public procedure Seek(pos: int64) := self.Pos := pos;
///Ставит файловый курсор на байт pos (нумеруя с 0) от начала файла
public procedure SeekByte(pos: int64) := self.PosByte := pos;
2018-11-08 00:24:28 +03:00
///Записывает все изменения в файл и отчищает внутренние буферы
2019-01-26 11:43:27 +03:00
///До вызова Flush или Close все изменения и кеш хранятся в оперативной памяти
///Поэтому, если вы проводите операции с большими объёмами памяти, лучше вызывать Flush время от времени
2018-11-08 00:24:28 +03:00
public procedure Flush;
2019-01-26 11:43:27 +03:00
2018-11-08 00:24:28 +03:00
///Сохраняет и закрывает файл, если он открыт
public procedure Close;
///Записывает один элемент одним блоком в файл
2019-01-26 11:43:27 +03:00
///Переставляет файловый курсор на 1 элемет вперёд
2018-11-08 00:24:28 +03:00
public procedure Write(o: T);
2019-01-26 11:43:27 +03:00
2018-11-08 00:24:28 +03:00
///Записывает массив элементов одним блоком в файл
2019-01-26 11:43:27 +03:00
///Переставляет файловый курсор на количество элементов, равному количеству элементов в переданном массиве
public procedure Write(params o: array of T);
2018-11-08 00:24:28 +03:00
///Записывает последовательность элементов, у которой можно узнать длину, одним блоком в файл
2019-01-26 11:43:27 +03:00
///Переставляет файловый курсор на количество элементов, равному количеству элементов в переданной коллекции
public procedure Write(o: ICollection<T>);
///Записывает последовательность элементов, у которой нельзя узнать длину, по 1 элементу в файл
///Переставляет файловый курсор на количество элементов, равному количеству элементов в переданной последовательности
public procedure Write(o: sequence of T);
///Записывает count элементов массива, начиная с элемента from, одним блоком в файл
///Переставляет файловый курсор на сount элеметов вперёд
public procedure Write(o: array of T; from, count: integer);
///Записывает count элементов последовательности, у которой можно узнать длину, начиная с элемента from, одним блоком в файл
///Переставляет файловый курсор на сount элеметов вперёд
public procedure Write(o: ICollection<T>; from, count: integer);
///Записывает count элементов последовательности, у которой нельзя узнать длину, начиная с элемента from, одним блоком в файл
///После каждого элемента переставляет файловый курсор на 1 элемет вперёд
public procedure Write(o: sequence of T; from, count: integer);
2018-11-08 00:24:28 +03:00
///Читает один элемент из файла одним блоком
2019-01-26 11:43:27 +03:00
///Переставляет файловый курсор на 1 элемет вперёд
public function Read: T;
///Читает массив из count элементов из файла одним блоком
///Переставляет файловый курсор на count элеметов вперёд
public function Read(count: integer): array of T;
///Читает массив из count элементов одним блоком, начиная с элемента start_elm
2018-11-08 00:24:28 +03:00
///И переставляет файловый курсор на count элеметов вперёд
2019-01-26 11:43:27 +03:00
public function Read(start_elm, count: integer): array of T;
2018-11-08 00:24:28 +03:00
///Возвращает ленивую последовательность из count элементов
2019-01-26 11:43:27 +03:00
///После завершения чтения курсор окажется на последнем элементе возвращаемой последовательности
///Каждый раз чтение будет происходить, начиная с элемента, на котором в данный момент стоит файловый курсор
public function ReadLazy(count: integer): sequence of T := InternalReadLazy(count, PosByte);
///Возвращает ленивую последовательность из count элементов, начиная с элемента start_elm
///После завершения чтения курсор окажется на последнем элементе возвращаемой последовательности
///Каждый раз чтение будет происходить, начиная с элемента start_elm
public function ReadLazy(start_elm, count: integer): sequence of T := InternalReadLazy(count, _offset + start_elm*sz);
2018-11-08 00:24:28 +03:00
///Возвращает ленивую последовательность из блоков-массивов с элементами типа T
2019-01-26 11:43:27 +03:00
///Каждый блок хранит такое количество байт, которое не превышает 4 КБ
///После прочтения каждого блока файловый корсор будет переставлен на его конец
public function ToSeqBlocks: sequence of array of T := ToSeqBlocks(4096);
2018-11-08 00:24:28 +03:00
///Возвращает ленивую последовательность из блоков-массивов с элементами типа T
2019-01-26 11:43:27 +03:00
///Каждый блок хранит такое количество байт, которое не превышает blocks_size
///После чтения каждого блока файловый корсор будет переставлен на его конец
public function ToSeqBlocks(blocks_size: integer): sequence of array of T;
///Возвращает ленивую последовательность из всех элементов, хранящихся в файле
///После прохода по элементам последовательности позиция в файле будет передвигаться на последний использованный элемент
public function ToSeq: sequence of T;
2018-11-08 00:24:28 +03:00
protected procedure Finalize; override;
begin
Close;
end;
end;
{$region Exception's}
type
FileNotAssignedException = class(Exception)
constructor :=
2019-01-26 11:43:27 +03:00
inherited Create($'Данная переменная не привязана к файлу{10}Используйте метод Assign');
2018-11-08 00:24:28 +03:00
end;
FileNotOpenedException = class(Exception)
constructor(fname:string) :=
2019-01-26 11:43:27 +03:00
inherited Create($'Файл {fname} не открыт, откройте его с помощью Open, Reset, Append или Rewrite');
2018-11-08 00:24:28 +03:00
end;
FileNotClosedException = class(Exception)
constructor(fname:string) :=
2019-01-26 11:43:27 +03:00
inherited Create($'Файл {fname} открыт, закройте его методом Close перед тем как продолжать');
2018-11-08 00:24:28 +03:00
end;
CannotReadAfterEOF = class(Exception)
constructor :=
2019-01-26 11:43:27 +03:00
inherited Create($'Нельзя читать за пределами файла');
2018-11-08 00:24:28 +03:00
end;
{$endregion Exception's}
implementation
{$region Linking}
2019-01-26 11:43:27 +03:00
procedure BlockFileBase.Link(f: BlockFileBase);
2018-11-08 00:24:28 +03:00
begin
if f.str = nil then raise new FileNotOpenedException($'{f.fi.FullName}, чью переменную передали в конструктор BlockFileOf<T>,');
foreach var l in f.linked + f do
begin
self.linked.Add(l);
l.linked.Add(self);
end;
self.str := f.str;
self.br := f.br;
self.bw := f.bw;
self.fi := new FileInfo(f.fi.FullName);
end;
procedure BlockFileBase.UnLink;
begin
foreach var l in self.linked do
l.linked.Remove(self);
self.linked.Clear;
end;
{$endregion Linking}
{$region property implementation}
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.GetName: string;
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
fi.Refresh;
Result := fi.Name;
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.GetFullName: string;
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
//fi.Refresh;//А тут не надо
Result := fi.FullName;
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.GetByteFileSize: int64;
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str <> nil then
begin
Result := str.Length;
exit;
end;
fi.Refresh;
Result := fi.Length;
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.SetByteFileSize(size: int64);
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
str.SetLength(size);
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.GetPosByte: int64;
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
Result := str.Position;
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.SetPosByte(pos: int64);
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
str.Position := pos;
end;
{$endregion property implementation}
{$region Setup IO}
{$region Basic}
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Assign(fname: string);
2018-11-08 00:24:28 +03:00
begin
if str <> nil then raise new FileNotClosedException(fi.FullName);
fi := new System.IO.FileInfo(fname);
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Assign(fname: string; offset: int64);
2018-11-08 00:24:28 +03:00
begin
Assign(fname);
_offset := offset;
end;
procedure BlockFileOf<T>.UnAssign;
begin
if str <> nil then raise new FileNotClosedException(fi.FullName);
fi := nil;
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Open(mode: FileMode);
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str <> nil then raise new FileNotClosedException(fi.FullName);
str := fi.Open(mode);
bw := new BinaryWriter(str);
br := new BinaryReader(str);
end;
procedure BlockFileOf<T>.Delete;
begin
if fi = nil then raise new FileNotAssignedException;
fi.Delete;
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.GetExists: boolean;
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
fi.Refresh;
Result := fi.Exists;
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Rename(NewName: string);
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str <> nil then raise new FileNotClosedException(fi.FullName);
fi.MoveTo(NewName);
end;
{$endregion Assign}
{$region Rewrite}
procedure BlockFileOf<T>.Rewrite :=
Open(FileMode.Create);
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Rewrite(fname: string);
2018-11-08 00:24:28 +03:00
begin
Assign(fname);
Rewrite;
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Rewrite(fname: string; offset: int64);
2018-11-08 00:24:28 +03:00
begin
Assign(fname, offset);
Rewrite;
end;
{$endregion Rewrite}
{$region Reset}
procedure BlockFileOf<T>.Reset :=
Open(FileMode.Open);
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Reset(fname: string);
2018-11-08 00:24:28 +03:00
begin
Assign(fname);
Reset;
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Reset(fname: string; offset: int64);
2018-11-08 00:24:28 +03:00
begin
Assign(fname, offset);
Reset;
end;
{$endregion Reset}
{$region Append}
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Append;
begin
Open(FileMode.Open);
str.Position := str.Length;
end;
2018-11-08 00:24:28 +03:00
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Append(fname: string);
2018-11-08 00:24:28 +03:00
begin
Assign(fname);
Append;
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Append(fname: string; offset: int64);
2018-11-08 00:24:28 +03:00
begin
Assign(fname, offset);
Append;
end;
{$endregion Append}
{$region Closing}
procedure BlockFileOf<T>.Flush;
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
str.Flush;
end;
procedure BlockFileOf<T>.Close;
begin
if str <> nil then
begin
if linked.Count <> 0 then
UnLink else
str.Close;
str := nil;
br := nil;
bw := nil;
end;
end;
{$endregion Closing}
{$endregion Setup IO}
{$region Write}
procedure BlockFileOf<T>.Write(o: T);
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
var a := new byte[sz];
var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned);
2019-04-01 07:15:39 +03:00
try
System.Buffer.MemoryCopy(
@o,
gc_hnd.AddrOfPinnedObject.ToPointer,
sz,sz
);
finally
gc_hnd.Free;
end;
2018-11-08 00:24:28 +03:00
bw.Write(a);
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Write(params o: array of T);
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
var bl := sz*o.Length;
var a := new byte[bl];
var gc_hnd1 := GCHandle.Alloc(o, GCHandleType.Pinned);
2019-04-01 07:15:39 +03:00
try
var gc_hnd2 := GCHandle.Alloc(a, GCHandleType.Pinned);
try
System.Buffer.MemoryCopy(
gc_hnd1.AddrOfPinnedObject.ToPointer,
gc_hnd2.AddrOfPinnedObject.ToPointer,
bl,bl
);
finally
gc_hnd2.Free;
end;
finally
gc_hnd1.Free;
end;
2018-11-08 00:24:28 +03:00
bw.Write(a);
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Write(o: ICollection<T>);
2018-11-08 00:24:28 +03:00
type TArr = array of T;
begin
if o is TArr(var a) then
begin
Write(a);
exit;
end;
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
var a := new byte[sz*o.Count];
var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned);
2019-04-01 07:15:39 +03:00
try
var hnd := gc_hnd.AddrOfPinnedObject;
foreach var el in o do
begin
System.Buffer.MemoryCopy(
@el,
hnd.ToPointer,
sz,sz
);
System.IntPtr.Add(hnd, sz);
end;
finally
gc_hnd.Free;
2018-11-08 00:24:28 +03:00
end;
bw.Write(a);
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Write(o: sequence of T) :=
2018-11-08 00:24:28 +03:00
if o is ICollection<T>(var c) then
Write(c) else
foreach var el in o do
Write(el);
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Write(o: array of T; from, count: integer);
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
var bl := sz*count;
var a := new byte[bl];
var gc_hnd1 := GCHandle.Alloc(o, GCHandleType.Pinned);
2019-04-01 07:15:39 +03:00
try
var gc_hnd2 := GCHandle.Alloc(a, GCHandleType.Pinned);
try
System.Buffer.MemoryCopy(
System.IntPtr.Add(gc_hnd1.AddrOfPinnedObject, from * sz).ToPointer,
gc_hnd2.AddrOfPinnedObject.ToPointer,
bl,bl
);
finally
gc_hnd2.Free;
end;
finally
gc_hnd1.Free;
end;
2018-11-08 00:24:28 +03:00
bw.Write(a);
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Write(o: ICollection<T>; from, count: integer);
2018-11-08 00:24:28 +03:00
type TArr = array of T;
begin
if o is TArr(var a) then
begin
Write(a, from, count);
exit;
end;
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
var a := new byte[sz*o.Count];
var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned);
2019-04-01 07:15:39 +03:00
try
var hnd := gc_hnd.AddrOfPinnedObject;
foreach var el in o do
if from > 0 then from -= 1 else
if count > 0 then
begin
System.Buffer.MemoryCopy(
@el,
hnd.ToPointer,
sz,sz
);
hnd := System.IntPtr.Add(hnd, sz);
count -= 1;
end;
finally
gc_hnd.Free;
end;
2018-11-08 00:24:28 +03:00
bw.Write(a);
end;
2019-01-26 11:43:27 +03:00
procedure BlockFileOf<T>.Write(o: sequence of T; from, count: integer) :=
2018-11-08 00:24:28 +03:00
if o is ICollection<T>(var c) then
Write(c,from,count) else
Write(o.Skip(from).Take(count));
{$endregion Write}
{$region Read}
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.Read: T;
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
if str.Length - str.Position < sz then raise new CannotReadAfterEOF;
var a := br.ReadBytes(sz);
var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned);
2019-04-01 07:15:39 +03:00
try
System.Buffer.MemoryCopy(
gc_hnd.AddrOfPinnedObject.ToPointer,
@Result,
sz,sz
);
finally
gc_hnd.Free;
end;
2018-11-08 00:24:28 +03:00
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.Read(count: integer): array of T;
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
if str.Length - str.Position < sz*count then raise new CannotReadAfterEOF;
var bl := sz*count;
var a := br.ReadBytes(bl);
Result := new T[count];
var gc_hnd1 := GCHandle.Alloc(a, GCHandleType.Pinned);
2019-04-01 07:15:39 +03:00
try
var gc_hnd2 := GCHandle.Alloc(Result, GCHandleType.Pinned);
try
System.Buffer.MemoryCopy(
gc_hnd1.AddrOfPinnedObject.ToPointer,
gc_hnd2.AddrOfPinnedObject.ToPointer,
bl,bl
);
finally
gc_hnd2.Free;
end;
finally
gc_hnd1.Free;
end;
2018-11-08 00:24:28 +03:00
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.Read(start_elm, count:integer): array of T;
2018-11-08 00:24:28 +03:00
begin
Pos := start_elm;
Result := Read(count);
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.InternalReadLazy(c: integer; start_pos: int64): sequence of T;
2018-11-08 00:24:28 +03:00
begin
if fi = nil then raise new FileNotAssignedException;
if str = nil then raise new FileNotOpenedException(fi.FullName);
if str.Length - start_pos < sz*c then raise new CannotReadAfterEOF;
for var i := 0 to c-1 do
begin
PosByte := start_pos + i*sz;
yield Read;
end;
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.ToSeqBlocks(blocks_size: integer): sequence of array of T;
2018-11-08 00:24:28 +03:00
begin
var c := blocks_size div sz;
var i := 0;
while true do
begin
var left := Size - i;
if left < c then
begin
if left > 0 then
begin
Pos := i;
yield Read(left);
end;
exit;
end else
begin
Pos := i;
yield Read(c);
i += c;
end;
end;
end;
2019-01-26 11:43:27 +03:00
function BlockFileOf<T>.ToSeq: sequence of T;
2018-11-08 00:24:28 +03:00
begin
var i := 0;
while not EOF do
begin
Pos := i;
yield Read;
i += 1;
end;
end;
{$endregion Read}
end.