This commit is contained in:
Бондарев Иван 2019-10-03 14:40:05 +02:00
parent 7f998cd371
commit f2df27dd87
3 changed files with 232 additions and 214 deletions

View file

@ -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
View 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.

View file

@ -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);
}
} }
//Теперь добавляем интерфейсы. //Теперь добавляем интерфейсы.
//Цикл с единицы, т.к. нулевой элемент уже был рассмотрен. //Цикл с единицы, т.к. нулевой элемент уже был рассмотрен.