bug fix #2119
This commit is contained in:
parent
7f998cd371
commit
f2df27dd87
|
|
@ -1,13 +1,14 @@
|
||||||
//*****************************************************************************************************\\
|
|
||||||
|
//*****************************************************************************************************\\
|
||||||
// Copyright (©) Cergey Latchenko ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega )
|
// Copyright (©) Cergey Latchenko ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega )
|
||||||
// This code is distributed under the Unlicense
|
// This code is distributed under the Unlicense
|
||||||
// For details see LICENSE.md file or this:
|
// For details see LICENSE file or this:
|
||||||
// https://github.com/SunSerega/PascalABC.Net-BlockFileOfT/blob/master/LICENSE.md
|
// https://github.com/SunSerega/POCGL/blob/master/LICENSE
|
||||||
//*****************************************************************************************************\\
|
//*****************************************************************************************************\\
|
||||||
// Copyright (©) Сергей Латченко ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega )
|
// Copyright (©) Сергей Латченко ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega )
|
||||||
// Этот код распространяется под Unlicense
|
// Этот код распространяется с лицензией Unlicense
|
||||||
// Для деталей смотрите в файл LICENSE.md или это:
|
// Подробнее в файле LICENSE или тут:
|
||||||
// https://github.com/SunSerega/PascalABC.Net-BlockFileOfT/blob/master/LICENSE.md
|
// https://github.com/SunSerega/POCGL/blob/master/LICENSE
|
||||||
//*****************************************************************************************************\\
|
//*****************************************************************************************************\\
|
||||||
|
|
||||||
///Модуль, содержащий тип BlockFileOf<T>
|
///Модуль, содержащий тип BlockFileOf<T>
|
||||||
|
|
@ -27,8 +28,6 @@ type
|
||||||
protected fi: FileInfo;
|
protected fi: FileInfo;
|
||||||
protected _offset: int64;
|
protected _offset: int64;
|
||||||
protected str: FileStream;
|
protected str: FileStream;
|
||||||
protected bw: BinaryWriter;
|
|
||||||
protected br: BinaryReader;
|
|
||||||
|
|
||||||
protected linked := new List<BlockFileBase>;
|
protected linked := new List<BlockFileBase>;
|
||||||
|
|
||||||
|
|
@ -41,69 +40,46 @@ type
|
||||||
end;
|
end;
|
||||||
|
|
||||||
///Тип, записывающий данные в файл по схожему с "file of <T>" принципу
|
///Тип, записывающий данные в файл по схожему с "file of <T>" принципу
|
||||||
///В отличие от типизированных файлов данный тип сохраняет всю запись одним блоком так как она записана в памяти.
|
///Но, в отличии от типизированных файлов, данный тип сохраняет всю запись одним блоком так, как она записа в памяти.
|
||||||
///Это даёт значительное преимущество по скорости, но ограничивает типы, которые могут быть использованы в качестве полей типа <T>
|
///Это даёт значительное преимущество по скорости, но ограничивает типы, которые могут быть использованы в качестве полей типа <T>
|
||||||
///
|
///
|
||||||
///В частности, поля записи T и всех вложенных записей не могут быть:
|
///Это значит, что поля записи T и всех вложенных записей не могут быть:
|
||||||
/// - Указателями
|
/// - Указателями
|
||||||
/// - Ссылочными типами (классами, динамическими массивами)
|
/// - Ссылочными типами (классами / динамическими массивами)
|
||||||
/// - Типами char и System.DateTime
|
///Однако, эти ограничения можно обойти, об этом в справке
|
||||||
///Эти ограничения можно обойти, об чем сказано в справке
|
BlockFileOf<T> = class(BlockFileBase) where T: record;
|
||||||
BlockFileOf<T> = class(BlockFileBase)
|
|
||||||
|
{$region Internal}
|
||||||
|
|
||||||
private static sz: integer;
|
private static sz: integer;
|
||||||
|
|
||||||
private static procedure TestForRefT(tt: System.Type);
|
private static procedure TestForRefT(tt: System.Type);
|
||||||
begin
|
begin
|
||||||
if tt.IsClass then
|
if tt = typeof(System.IntPtr) then exit; // IntPtr содержит 1 поле типа pointer. Но IntPtr это не указатель а число, с размером как у pointer
|
||||||
begin
|
|
||||||
MessageBox(new System.IntPtr(nil),
|
if tt.IsClass then raise new System.InvalidOperationException($'Тип {tt} ссылочный.{#10}Ссылочные типы нельзя сохранять в типизированный файл');
|
||||||
$'Тип {tt} ссылочный.{#10}Ссылочные типы нельзя сохранять в типизированный файл.{#10}Нажмите OK для выхода.',
|
|
||||||
$'Тип T из BlockFileOf<T> содержет ссылочные типы',
|
foreach var fi in tt.GetFields(
|
||||||
$10
|
|
||||||
);
|
|
||||||
Halt(-1);
|
|
||||||
end;
|
|
||||||
foreach var fi in
|
|
||||||
tt.GetFields(
|
|
||||||
System.Reflection.BindingFlags.GetField or
|
System.Reflection.BindingFlags.GetField or
|
||||||
System.Reflection.BindingFlags.Instance or
|
System.Reflection.BindingFlags.Instance or
|
||||||
System.Reflection.BindingFlags.Public or
|
System.Reflection.BindingFlags.Public or
|
||||||
System.Reflection.BindingFlags.NonPublic
|
System.Reflection.BindingFlags.NonPublic
|
||||||
) do
|
) do
|
||||||
if not fi.IsLiteral then
|
if not fi.IsLiteral then
|
||||||
if fi.FieldType <> tt then//integer имеет поле типа integer, без этой строчки StackOwerflow
|
if fi.FieldType <> tt then // тип integer имеет поле типа integer, без этой строчки StackOverflowException
|
||||||
TestForRefT(fi.FieldType);
|
TestForRefT(fi.FieldType);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
private static constructor :=
|
private static constructor :=
|
||||||
try
|
try
|
||||||
TestForRefT(typeof(T));
|
TestForRefT(typeof(T));
|
||||||
|
sz := Marshal.SizeOf&<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 содержит ссылочные типы',
|
|
||||||
$10
|
|
||||||
);
|
|
||||||
Halt(-1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
(**)
|
|
||||||
|
|
||||||
sz := Marshal.SizeOf(typeof(T));
|
|
||||||
except
|
except
|
||||||
on e:Exception do
|
on e:Exception do
|
||||||
begin
|
begin
|
||||||
MessageBox(new System.IntPtr(nil),
|
MessageBox(new System.IntPtr(nil),
|
||||||
e.ToString,
|
e.ToString,
|
||||||
$'При инициализации BlockFileOf<{typeof(T)}> произошла ошибка',
|
$'BlockFileOf<{typeof(T)}> не может инициализироваться:',
|
||||||
$10
|
$10
|
||||||
);
|
);
|
||||||
Halt(-1);
|
Halt(-1);
|
||||||
|
|
@ -126,7 +102,9 @@ type
|
||||||
private function GetPosByte: int64;
|
private function GetPosByte: int64;
|
||||||
private procedure SetPosByte(pos: int64);
|
private procedure SetPosByte(pos: int64);
|
||||||
|
|
||||||
private function InternalReadLazy(c: integer; start_pos: int64):sequence of T;
|
{$endregion Internal}
|
||||||
|
|
||||||
|
{$region constructor's}
|
||||||
|
|
||||||
///Инициализирует переменную файла, не привязывая её к файлу на диске
|
///Инициализирует переменную файла, не привязывая её к файлу на диске
|
||||||
public constructor := exit;
|
public constructor := exit;
|
||||||
|
|
@ -136,11 +114,11 @@ type
|
||||||
Assign(fname);
|
Assign(fname);
|
||||||
|
|
||||||
///Инициализирует переменную файла, привязывая её к файлу с именем fname
|
///Инициализирует переменную файла, привязывая её к файлу с именем fname
|
||||||
///Устанавливает значение смещения от начала в байтах на offset
|
///Устанавливает значение смещения от начала файла в байтах на offset
|
||||||
public constructor(fname: string; offset: int64) :=
|
public constructor(fname: string; offset: int64) :=
|
||||||
Assign(fname, offset);
|
Assign(fname, offset);
|
||||||
|
|
||||||
///- constructor BlockFileOf<>(f:BlockFileOf<>);
|
///- constructor BlockFileOf<>(f: BlockFileOf<>);
|
||||||
///Инициализирует новую переменную, создавая связку с заданной переменной
|
///Инициализирует новую переменную, создавая связку с заданной переменной
|
||||||
///После вызова этого конструктора переданная и созданная переменные будут использовать общий файловый поток, но записывать разные типы данных (у них может быть разный T)
|
///После вызова этого конструктора переданная и созданная переменные будут использовать общий файловый поток, но записывать разные типы данных (у них может быть разный T)
|
||||||
///Это значит, что переменная, которую передали в конструктор, уже должна иметь открытый файловый поток
|
///Это значит, что переменная, которую передали в конструктор, уже должна иметь открытый файловый поток
|
||||||
|
|
@ -148,6 +126,10 @@ type
|
||||||
public constructor(f: BlockFileBase) :=
|
public constructor(f: BlockFileBase) :=
|
||||||
Link(f);
|
Link(f);
|
||||||
|
|
||||||
|
{$endregion constructor's}
|
||||||
|
|
||||||
|
{$region property's}
|
||||||
|
|
||||||
///Возвращает или задаёт размер блока из одного элемента в байтах
|
///Возвращает или задаёт размер блока из одного элемента в байтах
|
||||||
///Задавать это свойство не рекомендуется
|
///Задавать это свойство не рекомендуется
|
||||||
public property TSize: integer read integer(sz) write sz := value;
|
public property TSize: integer read integer(sz) write sz := value;
|
||||||
|
|
@ -172,43 +154,39 @@ type
|
||||||
///Определяет, существует ли файл
|
///Определяет, существует ли файл
|
||||||
public property Exists: boolean read GetExists;
|
public property Exists: boolean read GetExists;
|
||||||
|
|
||||||
///Возвращает номер текущего элемета в файле (нумеруя с 0)
|
///Возвращает или задаёт номер текущего элемета в файле (нумеруя с 0)
|
||||||
public property Pos: int64 read GetPos write SetPos;
|
public property Pos: int64 read GetPos write SetPos;
|
||||||
|
|
||||||
///Возвращает номер текущего байта от начала файла (нумеруя с 0)
|
///Возвращает или задаёт номер текущего байта от начала файла (нумеруя с 0)
|
||||||
public property PosByte: int64 read GetPosByte write SetPosByte;
|
public property PosByte: int64 read GetPosByte write SetPosByte;
|
||||||
|
|
||||||
///Определяет, привязана ли переменная к файлу
|
///Определяет, привязана ли переменная к файлу
|
||||||
public property Assigned: boolean read fi <> nil;
|
public property Assigned: boolean read fi<>nil;
|
||||||
|
|
||||||
///Определяет, открыт ли файл
|
///Определяет, открыт ли файл
|
||||||
public property Opened: boolean read str <> nil;
|
public property Opened: boolean read str<>nil;
|
||||||
|
|
||||||
///Определяет, достигнут ли конец файла
|
///Определяет, достигнут ли конец файла
|
||||||
public property EOF: boolean read ByteSize-PosByte < sz;
|
public property EOF: boolean read ByteSize-PosByte < sz;
|
||||||
|
|
||||||
///Возвращает поток текущего файла (или nil если файл не открыт)
|
///Возвращает поток текущего файла (или nil если файл не открыт)
|
||||||
///Внимание! Любое действие, связанное с изменением данного потока файла, приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете
|
///Внимание! Любое действие, связанное с изменением данного потока файла приведёт к неожиданным последствиям, используйте его только если знаете, что вы делаете
|
||||||
public property BaseStream: FileStream read str;
|
public property BaseStream: FileStream read str;
|
||||||
|
|
||||||
///Возвращает BinaryWriter текущего файла (или nil если файл не открыт)
|
///Возвращает FileInfo текущего файла (или nil, если переменная не привязана к файлу)
|
||||||
///Внимание! Любое действие, связанное с изменением основного потока файла приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете
|
|
||||||
public property BinWriter: BinaryWriter read bw;
|
|
||||||
|
|
||||||
///Возвращает BinaryReader текущего файла (или nil если файл не открыт)
|
|
||||||
///Внимание! Любое действие, связанное с изменением основного потока файла приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете
|
|
||||||
public property BinReader: BinaryReader read br;
|
|
||||||
|
|
||||||
///Возвращает FileInfo текущего файла (или nil если переменная не привязана к файлу)
|
|
||||||
public property FileInfo: System.IO.FileInfo read fi;
|
public property FileInfo: System.IO.FileInfo read fi;
|
||||||
|
|
||||||
|
{$endregion property's}
|
||||||
|
|
||||||
|
{$region Setup IO}
|
||||||
|
|
||||||
///Привязывает текущий экземпляр BlockFileOf<T> к файлу с именем fname
|
///Привязывает текущий экземпляр BlockFileOf<T> к файлу с именем fname
|
||||||
///Привязывать можно и к несуществующим файлам, при открытии на запись будет создан новый файл
|
///Привязывать можно и к несуществующим файлам, при открытии на запись будет создан новый файл
|
||||||
public procedure Assign(fname: string);
|
public procedure Assign(fname: string);
|
||||||
|
|
||||||
///Привязывает данную переменную к файлу с именем fname
|
///Привязывает данную переменную к файлу с именем fname
|
||||||
///Устанавливает смещение от начала файла до начала элементов в байтах на offset
|
///Устанавливает смещение от начала файла до начала элементов в байтах на offset
|
||||||
///Привязывать можно и к несуществующим файлам, при открытии с использованием Rewrite будет создан новый файл
|
///Привязывать можно и к несуществующим файлам, при открытии чем то вроде Rewrite будет создан новый файл
|
||||||
public procedure Assign(fname: string; offset: int64);
|
public procedure Assign(fname: string; offset: int64);
|
||||||
|
|
||||||
///Удаляет связь переменной с файлом, если связь есть
|
///Удаляет связь переменной с файлом, если связь есть
|
||||||
|
|
@ -224,7 +202,7 @@ type
|
||||||
///При указании иного расположения файл будет перемещён
|
///При указании иного расположения файл будет перемещён
|
||||||
public procedure Rename(NewName: string);
|
public procedure Rename(NewName: string);
|
||||||
|
|
||||||
///Создает файл
|
///Создает (или обнуляет) файл
|
||||||
public procedure Rewrite;
|
public procedure Rewrite;
|
||||||
|
|
||||||
///Привязывает данную переменную к файлу с именем fname и создает (или обнуляет) его
|
///Привязывает данную переменную к файлу с именем fname и создает (или обнуляет) его
|
||||||
|
|
@ -254,20 +232,18 @@ type
|
||||||
///Устанавливает смещение от начала файла до начала элементов в байтах на offset
|
///Устанавливает смещение от начала файла до начала элементов в байтах на offset
|
||||||
public procedure Append(fname: string; offset: int64);
|
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;
|
|
||||||
|
|
||||||
///Записывает все изменения в файл и отчищает внутренние буферы
|
///Записывает все изменения в файл и отчищает внутренние буферы
|
||||||
///До вызова Flush или Close все изменения и кеш хранятся в оперативной памяти
|
///До вызова Flush или Close все изменения и кеш хранятся в оперативной памяти
|
||||||
///Поэтому, если вы проводите операции с большими объёмами памяти, лучше вызывать Flush время от времени
|
///Если вы проводите операции с большими объёмами памяти - рекомендуется вызывать Flush время от времени
|
||||||
public procedure Flush;
|
public procedure Flush;
|
||||||
|
|
||||||
///Сохраняет и закрывает файл, если он открыт
|
///Сохраняет и закрывает файл, если он открыт
|
||||||
public procedure Close;
|
public procedure Close;
|
||||||
|
|
||||||
|
{$endregion Setup IO}
|
||||||
|
|
||||||
|
{$region Write}
|
||||||
|
|
||||||
///Записывает один элемент одним блоком в файл
|
///Записывает один элемент одним блоком в файл
|
||||||
///Переставляет файловый курсор на 1 элемет вперёд
|
///Переставляет файловый курсор на 1 элемет вперёд
|
||||||
public procedure Write(o: T);
|
public procedure Write(o: T);
|
||||||
|
|
@ -296,6 +272,10 @@ type
|
||||||
///После каждого элемента переставляет файловый курсор на 1 элемет вперёд
|
///После каждого элемента переставляет файловый курсор на 1 элемет вперёд
|
||||||
public procedure Write(o: sequence of T; from, count: integer);
|
public procedure Write(o: sequence of T; from, count: integer);
|
||||||
|
|
||||||
|
{$endregion Write}
|
||||||
|
|
||||||
|
{$region Read}
|
||||||
|
|
||||||
///Читает один элемент из файла одним блоком
|
///Читает один элемент из файла одним блоком
|
||||||
///Переставляет файловый курсор на 1 элемет вперёд
|
///Переставляет файловый курсор на 1 элемет вперёд
|
||||||
public function Read: T;
|
public function Read: T;
|
||||||
|
|
@ -308,16 +288,45 @@ type
|
||||||
///И переставляет файловый курсор на count элеметов вперёд
|
///И переставляет файловый курсор на count элеметов вперёд
|
||||||
public function Read(start_elm, count: integer): array of T;
|
public function Read(start_elm, count: integer): array of T;
|
||||||
|
|
||||||
|
///Читает один элемент из файла одним блоком и записывает в уже существующую переменную
|
||||||
|
///Переставляет файловый курсор на 1 элемет вперёд
|
||||||
|
public procedure Read(var o: T);
|
||||||
|
|
||||||
|
///Читает o.Length элементов из файла одним блоком и записывает их в уже существующий массив
|
||||||
|
///Переставляет файловый курсор на o.Length элеметов вперёд
|
||||||
|
public procedure Read(o: array of T);
|
||||||
|
|
||||||
|
///Читает count элементов одним блоком, начиная с элемента start_elm
|
||||||
|
///Записывает их в массив o начиная с индекса arr_offset
|
||||||
|
///И переставляет файловый курсор на count элеметов вперёд
|
||||||
|
public procedure Read(o: array of T; start_elm, arr_offset, count: integer);
|
||||||
|
|
||||||
|
private function InternalReadLazy(c: integer; start_pos: int64): sequence of T;
|
||||||
|
|
||||||
///Возвращает ленивую последовательность из count элементов
|
///Возвращает ленивую последовательность из count элементов
|
||||||
///После завершения чтения курсор окажется на последнем элементе возвращаемой последовательности
|
///После завершения чтения курсор окажется на последнем элементе возвращаемой последовательности
|
||||||
///Каждый раз чтение будет происходить, начиная с элемента, на котором в данный момент стоит файловый курсор
|
///Перед чтением каждого элемента файловый курсор переставляется на start_elm+n
|
||||||
|
///Где start_elm - значение Pos на момент вызова ReadLazy, а n - количество уже считанных элементов
|
||||||
public function ReadLazy(count: integer): sequence of T := InternalReadLazy(count, PosByte);
|
public function ReadLazy(count: integer): sequence of T := InternalReadLazy(count, PosByte);
|
||||||
|
|
||||||
///Возвращает ленивую последовательность из count элементов, начиная с элемента start_elm
|
///Возвращает ленивую последовательность из count элементов, начиная с элемента start_elm
|
||||||
///После завершения чтения курсор окажется на последнем элементе возвращаемой последовательности
|
///После завершения чтения курсор окажется на последнем элементе возвращаемой последовательности
|
||||||
///Каждый раз чтение будет происходить, начиная с элемента start_elm
|
///Перед чтением каждого элемента файловый курсор переставляется на start_elm+n
|
||||||
|
///Где n - количество уже считанных элементов
|
||||||
public function ReadLazy(start_elm, count: integer): sequence of T := InternalReadLazy(count, _offset + start_elm*sz);
|
public function ReadLazy(start_elm, count: integer): sequence of T := InternalReadLazy(count, _offset + start_elm*sz);
|
||||||
|
|
||||||
|
{$endregion Read}
|
||||||
|
|
||||||
|
{$region Utils}
|
||||||
|
|
||||||
|
///Ставит позицию в файле на элемент pos (нумеруя с 0)
|
||||||
|
///Рекомендуется использовать свойство Pos вместо данного метода
|
||||||
|
public procedure Seek(pos: int64) := self.Pos := pos;
|
||||||
|
|
||||||
|
///Ставит файловый курсор на байт pos (нумеруя с 0) от начала файла
|
||||||
|
///Рекомендуется использовать свойство PosByte вместо данного метода
|
||||||
|
public procedure SeekByte(pos: int64) := self.PosByte := pos;
|
||||||
|
|
||||||
///Возвращает ленивую последовательность из блоков-массивов с элементами типа T
|
///Возвращает ленивую последовательность из блоков-массивов с элементами типа T
|
||||||
///Каждый блок хранит такое количество байт, которое не превышает 4 КБ
|
///Каждый блок хранит такое количество байт, которое не превышает 4 КБ
|
||||||
///После прочтения каждого блока файловый корсор будет переставлен на его конец
|
///После прочтения каждого блока файловый корсор будет переставлен на его конец
|
||||||
|
|
@ -337,6 +346,8 @@ type
|
||||||
Close;
|
Close;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{$endregion Utils}
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{$region Exception's}
|
{$region Exception's}
|
||||||
|
|
@ -352,7 +363,7 @@ type
|
||||||
end;
|
end;
|
||||||
FileNotClosedException = class(Exception)
|
FileNotClosedException = class(Exception)
|
||||||
constructor(fname:string) :=
|
constructor(fname:string) :=
|
||||||
inherited Create($'Файл {fname} открыт, закройте его методом Close перед тем как продолжать');
|
inherited Create($'Файл {fname} открыт, закройте его методом Close перед тем как продолжить');
|
||||||
end;
|
end;
|
||||||
CannotReadAfterEOF = class(Exception)
|
CannotReadAfterEOF = class(Exception)
|
||||||
constructor :=
|
constructor :=
|
||||||
|
|
@ -377,8 +388,6 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
self.str := f.str;
|
self.str := f.str;
|
||||||
self.br := f.br;
|
|
||||||
self.bw := f.bw;
|
|
||||||
self.fi := new FileInfo(f.fi.FullName);
|
self.fi := new FileInfo(f.fi.FullName);
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
@ -407,7 +416,7 @@ end;
|
||||||
function BlockFileOf<T>.GetFullName: string;
|
function BlockFileOf<T>.GetFullName: string;
|
||||||
begin
|
begin
|
||||||
if fi = nil then raise new FileNotAssignedException;
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
//fi.Refresh;//А тут не надо
|
//fi.Refresh; // А тут не надо
|
||||||
Result := fi.FullName;
|
Result := fi.FullName;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -417,12 +426,11 @@ function BlockFileOf<T>.GetByteFileSize: int64;
|
||||||
begin
|
begin
|
||||||
if fi = nil then raise new FileNotAssignedException;
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
if str <> nil then
|
if str <> nil then
|
||||||
|
Result := str.Length else
|
||||||
begin
|
begin
|
||||||
Result := str.Length;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
fi.Refresh;
|
fi.Refresh;
|
||||||
Result := fi.Length;
|
Result := fi.Length;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BlockFileOf<T>.SetByteFileSize(size: int64);
|
procedure BlockFileOf<T>.SetByteFileSize(size: int64);
|
||||||
|
|
@ -477,8 +485,6 @@ begin
|
||||||
if fi = nil then raise new FileNotAssignedException;
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
if str <> nil then raise new FileNotClosedException(fi.FullName);
|
if str <> nil then raise new FileNotClosedException(fi.FullName);
|
||||||
str := fi.Open(mode);
|
str := fi.Open(mode);
|
||||||
bw := new BinaryWriter(str);
|
|
||||||
br := new BinaryReader(str);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BlockFileOf<T>.Delete;
|
procedure BlockFileOf<T>.Delete;
|
||||||
|
|
@ -580,8 +586,6 @@ begin
|
||||||
UnLink else
|
UnLink else
|
||||||
str.Close;
|
str.Close;
|
||||||
str := nil;
|
str := nil;
|
||||||
br := nil;
|
|
||||||
bw := nil;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -589,6 +593,16 @@ end;
|
||||||
|
|
||||||
{$endregion Setup IO}
|
{$endregion Setup IO}
|
||||||
|
|
||||||
|
{$region IO Utils}
|
||||||
|
|
||||||
|
procedure CopyMem<T1,T2>(var o1: T1; var o2: T2; count: integer) :=
|
||||||
|
System.Buffer.MemoryCopy(
|
||||||
|
@o1, @o2,
|
||||||
|
count, count
|
||||||
|
);
|
||||||
|
|
||||||
|
{$endregion IO Utils}
|
||||||
|
|
||||||
{$region Write}
|
{$region Write}
|
||||||
|
|
||||||
procedure BlockFileOf<T>.Write(o: T);
|
procedure BlockFileOf<T>.Write(o: T);
|
||||||
|
|
@ -596,17 +610,8 @@ begin
|
||||||
if fi = nil then raise new FileNotAssignedException;
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
||||||
var a := new byte[sz];
|
var a := new byte[sz];
|
||||||
var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned);
|
CopyMem(o,a[0],sz);
|
||||||
try
|
str.Write(a,0,sz);
|
||||||
System.Buffer.MemoryCopy(
|
|
||||||
@o,
|
|
||||||
gc_hnd.AddrOfPinnedObject.ToPointer,
|
|
||||||
sz,sz
|
|
||||||
);
|
|
||||||
finally
|
|
||||||
gc_hnd.Free;
|
|
||||||
end;
|
|
||||||
bw.Write(a);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BlockFileOf<T>.Write(params o: array of T);
|
procedure BlockFileOf<T>.Write(params o: array of T);
|
||||||
|
|
@ -616,58 +621,31 @@ begin
|
||||||
|
|
||||||
var bl := sz*o.Length;
|
var bl := sz*o.Length;
|
||||||
var a := new byte[bl];
|
var a := new byte[bl];
|
||||||
var gc_hnd1 := GCHandle.Alloc(o, GCHandleType.Pinned);
|
CopyMem(o[0],a[0],bl);
|
||||||
try
|
str.Write(a,0,bl);
|
||||||
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;
|
|
||||||
bw.Write(a);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BlockFileOf<T>.Write(o: ICollection<T>);
|
procedure BlockFileOf<T>.Write(o: ICollection<T>);
|
||||||
type TArr = array of T;
|
|
||||||
begin
|
begin
|
||||||
if o is TArr(var a) then
|
|
||||||
begin
|
|
||||||
Write(a);
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if fi = nil then raise new FileNotAssignedException;
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
||||||
|
|
||||||
var a := new byte[sz*o.Count];
|
var bl := sz*o.Count;
|
||||||
var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned);
|
var a := new byte[bl];
|
||||||
try
|
|
||||||
var hnd := gc_hnd.AddrOfPinnedObject;
|
var p := 0;
|
||||||
foreach var el in o do
|
var enm: IEnumerator<T> := o.GetEnumerator();
|
||||||
|
while enm.MoveNext do
|
||||||
begin
|
begin
|
||||||
System.Buffer.MemoryCopy(
|
var v := enm.Current;
|
||||||
@el,
|
CopyMem(v,a[p],sz);
|
||||||
hnd.ToPointer,
|
p += sz;
|
||||||
sz,sz
|
|
||||||
);
|
|
||||||
System.IntPtr.Add(hnd, sz);
|
|
||||||
end;
|
end;
|
||||||
finally
|
|
||||||
gc_hnd.Free;
|
str.Write(a,0,bl);
|
||||||
end;
|
|
||||||
bw.Write(a);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BlockFileOf<T>.Write(o: sequence of T) :=
|
procedure BlockFileOf<T>.Write(o: sequence of T) :=
|
||||||
if o is ICollection<T>(var c) then
|
|
||||||
Write(c) else
|
|
||||||
foreach var el in o do
|
foreach var el in o do
|
||||||
Write(el);
|
Write(el);
|
||||||
|
|
||||||
|
|
@ -675,65 +653,37 @@ procedure BlockFileOf<T>.Write(o: array of T; from, count: integer);
|
||||||
begin
|
begin
|
||||||
if fi = nil then raise new FileNotAssignedException;
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
||||||
|
if from+count > o.Length then raise new System.IndexOutOfRangeException;
|
||||||
|
|
||||||
var bl := sz*count;
|
var bl := sz*count;
|
||||||
var a := new byte[bl];
|
var a := new byte[bl];
|
||||||
var gc_hnd1 := GCHandle.Alloc(o, GCHandleType.Pinned);
|
CopyMem(o[from],a[0],bl);
|
||||||
try
|
str.Write(a,0,bl);
|
||||||
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;
|
|
||||||
bw.Write(a);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BlockFileOf<T>.Write(o: ICollection<T>; from, count: integer);
|
procedure BlockFileOf<T>.Write(o: ICollection<T>; from, count: integer);
|
||||||
type TArr = array of T;
|
|
||||||
begin
|
begin
|
||||||
if o is TArr(var a) then
|
|
||||||
begin
|
|
||||||
Write(a, from, count);
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if fi = nil then raise new FileNotAssignedException;
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
||||||
|
if from+count > o.Count then raise new System.IndexOutOfRangeException;
|
||||||
|
|
||||||
var a := new byte[sz*o.Count];
|
var bl := sz*count;
|
||||||
var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned);
|
var a := new byte[bl];
|
||||||
try
|
|
||||||
var hnd := gc_hnd.AddrOfPinnedObject;
|
var p := 0;
|
||||||
foreach var el in o do
|
var enm: IEnumerator<T> := o.Skip(from).Take(count).GetEnumerator;
|
||||||
if from > 0 then from -= 1 else
|
while enm.MoveNext do
|
||||||
if count > 0 then
|
|
||||||
begin
|
begin
|
||||||
System.Buffer.MemoryCopy(
|
var v := enm.Current;
|
||||||
@el,
|
CopyMem(v,a[p],sz);
|
||||||
hnd.ToPointer,
|
p += sz;
|
||||||
sz,sz
|
|
||||||
);
|
|
||||||
hnd := System.IntPtr.Add(hnd, sz);
|
|
||||||
count -= 1;
|
|
||||||
end;
|
end;
|
||||||
finally
|
|
||||||
gc_hnd.Free;
|
str.Write(a,0,bl);
|
||||||
end;
|
|
||||||
bw.Write(a);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure BlockFileOf<T>.Write(o: sequence of T; from, count: integer) :=
|
procedure BlockFileOf<T>.Write(o: sequence of T; from, count: integer) :=
|
||||||
if o is ICollection<T>(var c) then
|
Write(o.Skip(from).Take(count));
|
||||||
Write(c,from,count) else
|
|
||||||
Write(o.Skip(from).Take(count));
|
|
||||||
|
|
||||||
{$endregion Write}
|
{$endregion Write}
|
||||||
|
|
||||||
|
|
@ -745,17 +695,10 @@ begin
|
||||||
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
||||||
if str.Length - str.Position < sz then raise new CannotReadAfterEOF;
|
if str.Length - str.Position < sz then raise new CannotReadAfterEOF;
|
||||||
|
|
||||||
var a := br.ReadBytes(sz);
|
var a := new byte[sz];
|
||||||
var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned);
|
str.Read(a,0,sz);
|
||||||
try
|
CopyMem(a[0],Result,sz);
|
||||||
System.Buffer.MemoryCopy(
|
|
||||||
gc_hnd.AddrOfPinnedObject.ToPointer,
|
|
||||||
@Result,
|
|
||||||
sz,sz
|
|
||||||
);
|
|
||||||
finally
|
|
||||||
gc_hnd.Free;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function BlockFileOf<T>.Read(count: integer): array of T;
|
function BlockFileOf<T>.Read(count: integer): array of T;
|
||||||
|
|
@ -765,48 +708,90 @@ begin
|
||||||
if str.Length - str.Position < sz*count then raise new CannotReadAfterEOF;
|
if str.Length - str.Position < sz*count then raise new CannotReadAfterEOF;
|
||||||
|
|
||||||
var bl := sz*count;
|
var bl := sz*count;
|
||||||
var a := br.ReadBytes(bl);
|
var a := new byte[bl];
|
||||||
|
str.Read(a,0,bl);
|
||||||
Result := new T[count];
|
Result := new T[count];
|
||||||
var gc_hnd1 := GCHandle.Alloc(a, GCHandleType.Pinned);
|
CopyMem(a[0],Result[0],bl);
|
||||||
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;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function BlockFileOf<T>.Read(start_elm, count:integer): array of T;
|
function BlockFileOf<T>.Read(start_elm, count:integer): array of T;
|
||||||
begin
|
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;
|
||||||
|
|
||||||
Pos := start_elm;
|
Pos := start_elm;
|
||||||
Result := Read(count);
|
Result := Read(count);
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure BlockFileOf<T>.Read(var o: T);
|
||||||
|
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 := new byte[sz];
|
||||||
|
str.Read(a,0,sz);
|
||||||
|
CopyMem(a[0],o,sz);
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure BlockFileOf<T>.Read(o: array of T);
|
||||||
|
begin
|
||||||
|
var bl := sz*o.Length;
|
||||||
|
|
||||||
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
|
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
||||||
|
if str.Length - str.Position < bl then raise new CannotReadAfterEOF;
|
||||||
|
|
||||||
|
var a := new byte[bl];
|
||||||
|
str.Read(a,0,bl);
|
||||||
|
CopyMem(a[0],o[0],bl);
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure BlockFileOf<T>.Read(o: array of T; start_elm, arr_offset, count: integer);
|
||||||
|
begin
|
||||||
|
var bl := sz*count;
|
||||||
|
|
||||||
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
|
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
||||||
|
if str.Length - str.Position < bl then raise new CannotReadAfterEOF;
|
||||||
|
if arr_offset+count > o.Length then raise new System.IndexOutOfRangeException;
|
||||||
|
|
||||||
|
Pos := start_elm;
|
||||||
|
var a := new byte[bl];
|
||||||
|
str.Read(a,0,bl);
|
||||||
|
CopyMem(a[0],o[start_elm],bl);
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$endregion Read}
|
||||||
|
|
||||||
|
{$region Utils}
|
||||||
|
|
||||||
function BlockFileOf<T>.InternalReadLazy(c: integer; start_pos: int64): sequence of T;
|
function BlockFileOf<T>.InternalReadLazy(c: integer; start_pos: int64): sequence of T;
|
||||||
begin
|
begin
|
||||||
if fi = nil then raise new FileNotAssignedException;
|
if fi = nil then raise new FileNotAssignedException;
|
||||||
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
if str = nil then raise new FileNotOpenedException(fi.FullName);
|
||||||
if str.Length - start_pos < sz*c then raise new CannotReadAfterEOF;
|
if str.Length - start_pos < sz*c then raise new CannotReadAfterEOF;
|
||||||
|
|
||||||
for var i := 0 to c-1 do
|
loop c do
|
||||||
begin
|
begin
|
||||||
PosByte := start_pos + i*sz;
|
PosByte := start_pos;
|
||||||
yield Read;
|
yield Read;
|
||||||
|
start_pos += sz;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function BlockFileOf<T>.ToSeqBlocks(blocks_size: integer): sequence of array of T;
|
function BlockFileOf<T>.ToSeqBlocks(blocks_size: integer): sequence of array of T;
|
||||||
begin
|
begin
|
||||||
var c := blocks_size div sz;
|
var c := blocks_size div sz;
|
||||||
var i := 0;
|
var i := 0;
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
begin
|
begin
|
||||||
var left := Size - i;
|
var left := Size - i;
|
||||||
|
|
@ -825,20 +810,24 @@ begin
|
||||||
yield Read(c);
|
yield Read(c);
|
||||||
i += c;
|
i += c;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function BlockFileOf<T>.ToSeq: sequence of T;
|
function BlockFileOf<T>.ToSeq: sequence of T;
|
||||||
begin
|
begin
|
||||||
var i := 0;
|
var i := 0;
|
||||||
|
|
||||||
while not EOF do
|
while not EOF do
|
||||||
begin
|
begin
|
||||||
Pos := i;
|
Pos := i;
|
||||||
yield Read;
|
yield Read;
|
||||||
i += 1;
|
i += 1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{$endregion Read}
|
{$endregion Utils}
|
||||||
|
|
||||||
end.
|
end.
|
||||||
23
TestSuite/generics30.pas
Normal file
23
TestSuite/generics30.pas
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
type
|
||||||
|
TErr<T> = class;
|
||||||
|
|
||||||
|
TBase = abstract class
|
||||||
|
public static procedure p1<T>(temp: TErr<T>);
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
TErr<T> = class(TBase) end;
|
||||||
|
|
||||||
|
static procedure TBase.p1<T>(temp: TErr<T>);
|
||||||
|
begin
|
||||||
|
var q: TErr<T> := new TErr<T>;
|
||||||
|
var a1: TBase := q;
|
||||||
|
var a2: TBase := q as TBase;
|
||||||
|
assert(a2 <> nil);
|
||||||
|
q := a1 as TErr<T>;
|
||||||
|
assert(q <> nil);
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
TBase.p1&<integer>(nil);
|
||||||
|
end.
|
||||||
|
|
@ -3684,6 +3684,12 @@ namespace PascalABCCompiler.TreeConverter
|
||||||
AddError(get_location(_class_definition.class_parents.types[0]), "CAN_NOT_INHERIT_FROM_GENERIC_PARAMETER");
|
AddError(get_location(_class_definition.class_parents.types[0]), "CAN_NOT_INHERIT_FROM_GENERIC_PARAMETER");
|
||||||
}
|
}
|
||||||
context.converted_type.SetBaseType(tn);
|
context.converted_type.SetBaseType(tn);
|
||||||
|
var type_instances = generic_convertions.get_type_instances(context.converted_type);
|
||||||
|
if (type_instances != null)
|
||||||
|
foreach (generic_type_instance_info gti in type_instances)
|
||||||
|
{
|
||||||
|
gti.pseudo_instance.SetBaseType(tn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//Теперь добавляем интерфейсы.
|
//Теперь добавляем интерфейсы.
|
||||||
//Цикл с единицы, т.к. нулевой элемент уже был рассмотрен.
|
//Цикл с единицы, т.к. нулевой элемент уже был рассмотрен.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue