diff --git a/Configuration/GlobalAssemblyInfo.cs b/Configuration/GlobalAssemblyInfo.cs index fa00ee726..ef9b096a0 100644 --- a/Configuration/GlobalAssemblyInfo.cs +++ b/Configuration/GlobalAssemblyInfo.cs @@ -15,7 +15,7 @@ internal static class RevisionClass public const string Major = "3"; public const string Minor = "8"; public const string Build = "2"; - public const string Revision = "3059"; + public const string Revision = "3061"; public const string MainVersion = Major + "." + Minor; public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision; diff --git a/Configuration/Version.defs b/Configuration/Version.defs index 321443b3c..1038f927f 100644 --- a/Configuration/Version.defs +++ b/Configuration/Version.defs @@ -1,4 +1,4 @@ %MINOR%=8 -%REVISION%=3059 +%REVISION%=3061 %COREVERSION%=2 %MAJOR%=3 diff --git a/InstallerSamples/BlockFileOfT/FileDatabase/FileArray.pas b/InstallerSamples/BlockFileOfT/FileDatabase/FileArray.pas deleted file mode 100644 index 4591f2a3d..000000000 --- a/InstallerSamples/BlockFileOfT/FileDatabase/FileArray.pas +++ /dev/null @@ -1,174 +0,0 @@ -///Модуль с массивом, хранящим данные в файле -unit FileArray; - -interface - -uses BlockFileOfT; - -type - /// Массив, хранящий данные в файле - /// Намного медленнее обычного массива, однако может хранить бОльший объём данных - /// T может быть только записью, не содержащей ссылочные поля (указатели, классы и т.п.) - FileArr = sealed class - where T: record; - - private f: BlockFileOf; - ///Автоматически очищает кэш после каждого чтения и записи - public AutoFlush := true; - ///Указывает, удалять ли файл, когда сборщик мусора удалит данный объект - public DeleteOnExit: boolean; - - - private function GetItem32(i: integer): T; - private function GetItem64(i: int64): T; - private function GetSection32(from, c: integer): array of T; - private function GetSection64(from, c: int64): array of T; - private function GetSectionLazy32(from, c: integer): sequence of T; - private function GetSectionLazy64(from, c: int64): sequence of T; - - private procedure SetItem32(i: integer; o: T); - private procedure SetItem64(i: int64; o: T); - private procedure SetSection32(from, c: integer; o: array of T); - private procedure SetSection64(from, c: int64; o: array of T); - private procedure SetSectionLazy32(from, c: integer; o: sequence of T); - private procedure SetSectionLazy64(from, c: int64; o: sequence of T); - - - - public property InnerFile: BlockFileOf read f; - - ///Возвращает количество элементов. В отличии от array of T - длину можно устанавливать - public property Length: int64 read f.Size write f.Size := value; - - ///Возвращает или задаёт элемент с индексом i - public property Item[i: integer]: T read GetItem32 write SetItem32; - ///Возвращает или задаёт элемент с индексом i - public property Item64[i: int64]: T read GetItem64 write SetItem64; default; - - ///Срез начиная с элемента from и длиной count - public property Section[from, count: integer]: array of T read GetSection32 write SetSection32; - ///Срез начиная с элемента from и длиной count - public property Section64[from, count: int64]: array of T read GetSection64 write SetSection64; - - ///Возвращает ленивую последовательность начиная с элемента from и длиной count - ///Устанавливает значение элементов начиная с элемента from и длиной count - public property SectionLazy[from, count: integer]: sequence of T read GetSectionLazy32 write SetSectionLazy32; - ///Возвращает ленивую последовательность начиная с элемента from и длиной count - ///Устанавливает значение элементов начиная с элемента from и длиной count - public property SectionLazy64[from, count: int64]: sequence of T read GetSectionLazy64 write SetSectionLazy64; - - - - ///Очищает кэш чтения/записи - public procedure Flush := f.BaseStream.Flush; - - - ///Создаёт новый массив, хранящий данные в файле fname. Если его нет - создаёт новый - public constructor(fname: string) := - Create(fname, System.IO.FileMode.OpenOrCreate); - - ///Создаёт новый массив хранящий данные в файле fname. - ///mode указывает каким образом открывать/создавать файл и имеет тип System.IO.FileMode - public constructor(fname: string; mode: System.IO.FileMode); - begin - f := new BlockFileOf(fname); - f.Open(mode); - end; - - ///Использует заданный файл как основу для массива - ///Если файл не открыт - его откроет в режиме System.IO.FileMode.OpenOrCreate - public constructor(&file: BlockFileOf); - begin - f := &file; - if not f.Opened then - f.Open(System.IO.FileMode.OpenOrCreate); - end; - - ///Открывает заданный файл в режиме mode и использует его как основу для массива - public constructor(&file: BlockFileOf; mode: System.IO.FileMode); - begin - f := &file; - f.Open(mode); - end; - - /// Создаёт новый массив хранящий данные в новом временном файл (в системной папке) - /// Созданный файл будет удалён в произвольное время после окончания использования переменной или при вызове Finalize - public constructor; - begin - Create(System.IO.Path.GetTempFileName, System.IO.FileMode.CreateNew); - DeleteOnExit := true; - end; - - ///Выполняет необходимую очистку при завершении работы с переменной - ///Этот метод выполнится автоматически в произвольное время (после окончания использования переменной) - ///Если попытаться использовать переменную после вызова этого метода - поведение будет неопределённым - public procedure Finalize; override; - begin - f.Close; - if DeleteOnExit then f.Delete; - end; - - end; - -implementation - -{$region index property's} - -function FileArr.GetItem32(i: integer) := GetItem64(i); - -function FileArr.GetSection32(from, c: integer) := GetSection64(from, c); - -function FileArr.GetSectionLazy32(from, c: integer) := GetSectionLazy64(from, c); - -procedure FileArr.SetItem32(i: integer; o: T) := SetItem64(i, o); - -procedure FileArr.SetSection32(from, c: integer; o: array of T) := SetSection64(from, c, o); - -procedure FileArr.SetSectionLazy32(from, c: integer; o: sequence of T) := SetSectionLazy64(from, c, o); - -function FileArr.GetItem64(i: int64): T; -begin - f.Seek(i); - Result := f.Read; - if AutoFlush then f.BaseStream.Flush; -end; - -function FileArr.GetSection64(from, c: int64): array of T; -begin - f.Seek(from); - Result := f.Read(c); - if AutoFlush then f.BaseStream.Flush; -end; - -function FileArr.GetSectionLazy64(from, c: int64): sequence of T; -begin - f.Seek(from); - Result := f.ReadLazy(c); - if AutoFlush then f.BaseStream.Flush; -end; - -procedure FileArr.SetItem64(i: int64; o: T); -begin - f.Seek(i); - f.Write(o); - if AutoFlush then f.BaseStream.Flush; -end; - -procedure FileArr.SetSection64(from, c: int64; o: array of T); -begin - f.Seek(from); - if c > o.Length then raise new System.IndexOutOfRangeException('count должно быть <= длинны переданного массива'); - f.Write(o, 0, c); - if AutoFlush then f.BaseStream.Flush; -end; - -procedure FileArr.SetSectionLazy64(from, c: int64; o: sequence of T); -begin - f.Seek(from); - f.Write(o, 0, c); - if AutoFlush then f.BaseStream.Flush; -end; - -{$endregion index property's} - -end. \ No newline at end of file diff --git a/InstallerSamples/BlockFileOfT/FileDatabase/База данных на файле.pas b/InstallerSamples/BlockFileOfT/FileDatabase/База данных на файле.pas deleted file mode 100644 index 9eb5ec277..000000000 --- a/InstallerSamples/BlockFileOfT/FileDatabase/База данных на файле.pas +++ /dev/null @@ -1,201 +0,0 @@ -uses BlockFileOfT; -uses System.Runtime.InteropServices;//нужно чтоб не писать System.Runtime.InteropServices.StructLayout и т.п. -uses FileArray; - -type - DataType1 = record - b: byte; - - constructor(b:byte) := - self.b := b; - - end; - DataType2 = record - i: integer; - - constructor(i:integer) := - self.i := i; - - end; - DataType3 = record - ch: char; - - constructor(ch:char) := - self.ch := ch; - - end; - DataType4 = record - r: real; - - constructor(r:real) := - self.r := r; - - end; - - DataType = ( - ByteData = 1, - IntData = 2, - CharData = 3, - RealData = 4 - ); - - [StructLayout(LayoutKind.&Explicit)] // Позволяет явно указывать позицию каждого поля - DataUnit = record - [FieldOffset(0)] DataT: byte; - [FieldOffset(8)] DataT1: DataType1; // У этих 4 полей одинаковая позиция - [FieldOffset(8)] DataT2: DataType2; // Значит, у них будет общая память - [FieldOffset(8)] DataT3: DataType3; // Но это так же значит что если записать данные 1 типа - - [FieldOffset(8)] DataT4: DataType4; // данные другого типа считать не выйдет (выведет мусор) - - constructor(data: DataType1); - begin - DataT := 1; - DataT1 := data; - end; - - constructor(data: DataType2); - begin - DataT := 2; - DataT2 := data; - end; - - constructor(data: DataType3); - begin - DataT := 3; - DataT3 := data; - end; - - constructor(data: DataType4); - begin - DataT := 4; - DataT4 := data; - end; - - function ToString:string; override; - begin - var sb := new StringBuilder; - - sb.AppendFormat( - 'DataUnit({0,8}, ', - System.Enum.GetName(typeof(DataType), DataT) - ); - - case DataType(DataT) of - ByteData: sb.AppendFormat('{0})',DataT1.b); - IntData : sb.AppendFormat('{0})',DataT2.i); - CharData: sb.AppendFormat('{0})',DataT3.ch); - RealData: sb.AppendFormat('{0})',DataT4.r); - else raise new System.FormatException($'Не правильный тип данных: {DataT}'); - end; - - Result := sb.ToString; - end; - - end; - - Database = class(System.IDisposable) - private const HeaderSize = 1 + 4*2; // 1 байт на версию + 4 символа (по 2 байта) на DatabaseType - - public version: byte; - public DatabaseType: string; - public BlockFile: BlockFileOf; - public Data: FileArr; - - public constructor := exit; - - public constructor(fname: string; version: byte; DatabaseType: string); - begin - self.version := version; - self.DatabaseType := DatabaseType; - - self.BlockFile := new BlockFileOf(fname); - self.BlockFile.Offset := HeaderSize; - self.BlockFile.Open(System.IO.FileMode.Create); - - self.Data := new FileArr(self.BlockFile); - end; - - public class function Load(fname: string): Database; - begin - Result := new Database; - - Result.BlockFile := new BlockFileOf(fname, HeaderSize); - Result.BlockFile.Reset; - - begin // заголовок - var br := new System.IO.BinaryReader(Result.BlockFile.BaseStream); - - Result.version := br.ReadByte; - Result.DatabaseType := string.Create(ArrGen(4,i->char(br.ReadUInt16))).TrimEnd('_'); - - end; - - Result.Data := new FileArr(Result.BlockFile); - - Result.Data.AutoFlush := false; - Result.Data.DeleteOnExit := false; - - end; - - public procedure Save; - begin - - begin // заголовок - self.BlockFile.PosByte := 0; - var bw := new System.IO.BinaryWriter(self.BlockFile.BaseStream); - - bw.Write(self.version); - - foreach var ch in self.DatabaseType.PadRight(4,'_') do - bw.Write(word(ch)); - - end; - - self.Data.Flush; - end; - - public function ToString:string; override; - begin - var sb := new StringBuilder; - sb += $'version: {version}{#10}'; - sb += $'Database Type: {DatabaseType}{#10}'; - sb += $'Data:[{#10}'; - - BlockFile.Pos := 0; - foreach var du in BlockFile.Read(BlockFile.Size) do - sb += $'{#9}{du}{#10}'; - - sb += $']'; - Result := sb.ToString; - end; - - public procedure Dispose; - begin - Data.Finalize; - end; - - end; - -begin - var db1 := new Database('temp.bin',1,'JSBD'); // JSBD = Just Some Basic Data - - db1.version := 5; // Была 1, изменили на 5 - db1.Data[0] := new DataUnit(new DataType1(123)); - db1.Data[1] := new DataUnit(new DataType2(456)); - db1.Data[2] := new DataUnit(new DataType3('A')); - db1.Data[3] := new DataUnit(new DataType4(123.456)); - - db1.Save; - db1.Dispose; - - - - db1 := Database.Load('temp.bin'); - writeln(db1); - - writeln; - // Пытаемся прочитать переменную типа integer там - где записана переменная типа real - // Это, конечно, выводит мусор - writeln('int from real: ', db1.Data[3].DataT2.i); - -end. \ No newline at end of file diff --git a/InstallerSamples/BlockFileOfT/Дополнительно.pas b/InstallerSamples/BlockFileOfT/Дополнительно.pas deleted file mode 100644 index 156000c6e..000000000 --- a/InstallerSamples/BlockFileOfT/Дополнительно.pas +++ /dev/null @@ -1,79 +0,0 @@ -uses BlockFileOfT; - -uses System.Runtime.InteropServices; // для StructLayout у r3 - -type - r1 = record - b1, b2: byte; - - constructor(b1, b2: byte); - begin - self.b1 := b1; - self.b2 := b2; - end; - - ///Переопределение того - как объекты типа r1 будет выписывать writeln - function ToString: string; override := - $'r1({b1}, {b2})'; - - end; - - r2 = record - b: byte; - i: int64; - end; - - [StructLayout(LayoutKind.&Explicit, Size=9)] - r3 = record - [FieldOffset(0)] b: byte; - // Явно указываем что i будет хранится на следующем байте после b - // Так можно даже накладывать поля друг на друга - // Но поэтому также нужно относиться с осторожностью к такой возможности - // И помнить у какого типа какой размер, чтобы наложение полей не получилось там, где оно не требуется - [FieldOffset(1)] i: int64; - end; - -begin - var f := new BlockFileOf('temp.bin'); - f.Rewrite; - - f.Write(new r1(1, 2)); - f.Write(new r1(3, 4)); - f.Write(new r1(5, 6)); - - f.Pos := 1; - var str := f.BaseStream; - var br := new System.IO.BinaryReader(str); - Writeln(br.ReadByte); // 3 - потому что прочитало 1 байт, когда файловый курсор стоял в начале элемента #1 - - // А вот так делать не следует. Сейчас курсор находится в середине второго элемента - // Обычно это вызовет неопределённое поведение и заполнит поля полученной записи мусором - // Но этот случай простой, поэтому точно известно что прочитает половину второй и половину третьей записи и выведет (4,5) - Writeln(f.Read); - // Если вы НЕ пытаетесь специально читать мимо элементов - стоит устанавливать позицию в файле (f.Pos := ...) после прямой работы с BaseStream - - f.Pos := 0; - f.PosByte += 1; // сдвигаем курсор на один байт (вся запись f.TSize байт, что, в данном случае, 2) - // br и f всё ещё работают над тем же потоком, потому что мы не закрывали файл - // А файловый курсор хранится как раз в потоке - // Поэтому br можно всё ещё использовать и он будет синхронизирован с f.PosByte - Writeln(br.ReadByte); // 2, потому что второй байт первой записи - - f.Close; - - - - - var f2 := new BlockFileOf; - // f2.TSize это то же самое, что sizeof(r2) - // У меня 16, хотя sizeof(byte)=1 + sizeof(int64)=8, то есть должно быть 9? - // Это потому, что на многих процессорах добавляет отступы (так легче читать/записывать значения) в некоторые записи, так что будьте осторожны - // Это также хорошая причина не пользоваться бездумно BaseStream - потому что на разных компьютерах один и тот же код может работать по-разному - Writeln(f2.TSize); - - var f3 := new BlockFileOf; - // А вот теперь размер 9, потому что мы явно указали что отступ нам не нужен - // Конечно, теперь чтение/запись i может быть медленнее на некоторых процессорах, отступ всё же не для красоты был - Writeln(f3.TSize); - -end. \ No newline at end of file diff --git a/InstallerSamples/BlockFileOfT/Основы.pas b/InstallerSamples/BlockFileOfT/Основы.pas deleted file mode 100644 index 4c134b24c..000000000 --- a/InstallerSamples/BlockFileOfT/Основы.pas +++ /dev/null @@ -1,67 +0,0 @@ -uses BlockFileOfT; - -type - r1=record - i:integer; - r:real; - - constructor(i:integer; r:real); - begin - self.i := i; - self.r := r; - end; - ///Переопределение того - как объекты типа r1 будет выписывать writeln - function ToString:string; override := - $'r1({i}, {r})'; - end; - -begin -// var f: BlockFileOf; -// f := new BlockFileOf; // В отличии от file of T - BlockFileOf всегда надо инициализировать перед тем как использовать -// f.Assign('temp.bin'); - var f := new BlockFileOf('temp.bin'); // эта строчка работает как предыдущие 3 - - f.Rewrite; // Если навести мышку на метод (в данном случае .Rewrite) - показывает описание - f.Write(new r1(123,123.456)); - f.Write(new r1(456,456.789)); - -// f.Flush; // не обязательно - .Close вызывает .Flush перед тем как закрыть файл - // После работы с файлом - надо его закрыть. Иначе: - // 1. Часть данных может не сохраниться - // 2. Изменять файл вне переменной f будет нельзя, пока f держит его открытым - f.Close; - -// f.Assign('temp.bin'); // Не обязательно, файл остаётся привязан после закрытия - - f.Reset; - writeln($'Read#1: {f.Read}'); // r1(123,123.456) - writeln($'Pos#1: {f.Pos}'); // 1, потому что мы открыли файл через ".Reset" (курсор в начале файла) и затем прочитали 1 элемент - - // В отличии от file of T, у BlockFileOf нету режима чтения и режима записи - // Они объеденены, поэтому не надо закрывать файл чтобы записать что-то после чтения - f.Write(new r1(0,0)); - - writeln($'Size#1: {f.Size}'); // 2, потому что r1(0,0) НЕ втиснуло между первыми 2 элементами, а записало поверх второго - - writeln($'EOF: {f.EOF}'); // True, потому что у нас всего было 2 элемента и курсор сейчас стоит на позиции 2 (1 чтение + 1 запись) - -// f.Seek(1); - f.Pos := 1; // То же самое, что и .Seek(1), но лучше - - writeln($'Read#2: {f.Read}'); // r1(0,0), то, что мы только что записали - - f.Pos := 5; // Можно ставить за границей файла -// f.Read; // Ошибка: Нельзя читать за пределами файла. Можно только записывать - f.Write(new r1); - writeln($'Size#2: {f.Size}'); // 6, потому что мы записали +1 элемент после позиции 5 - - // Но вообще, размер правильно менять так: - f.Size := 10; - writeln($'Size#3: {f.Size}'); // 10 - writeln($'Pos#2: {f.Pos}'); // 6, как и была после того, как мы последний раз вызвали .Write - - // Программа сейчас завершится, поэтому файл закроется сам - // Но если не вызвать хотя бы Flush, то изменения могут не сохранится - f.Flush; - -end. \ No newline at end of file diff --git a/InstallerSamples/BlockFileOfT/Справка.html b/InstallerSamples/BlockFileOfT/Справка.html deleted file mode 100644 index b2af63627..000000000 --- a/InstallerSamples/BlockFileOfT/Справка.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - Выберете раздел: - -
- Если найдёте что-то неправильное или ошибку (в том числе и грамматическую) в справке - пишите сюда. - - - \ No newline at end of file diff --git a/InstallerSamples/BlockFileOfT/СправкаData/RefTSaving.html b/InstallerSamples/BlockFileOfT/СправкаData/RefTSaving.html deleted file mode 100644 index a1470c91e..000000000 --- a/InstallerSamples/BlockFileOfT/СправкаData/RefTSaving.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - - - - - - <=
-
- Сохранение ссылочных типов в блочный файл.
-
- - - -

В чём проблема ссылочных типов

- - - - -

Размерные массивы и строки

- - - - - - - \ No newline at end of file diff --git a/InstallerSamples/BlockFileOfT/СправкаData/WhyOffset.html b/InstallerSamples/BlockFileOfT/СправкаData/WhyOffset.html deleted file mode 100644 index 2e69dbd19..000000000 --- a/InstallerSamples/BlockFileOfT/СправкаData/WhyOffset.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - <=
-
-
- - -

Что такое "Offset"

- - - - -

Заголовки

- - - - - - \ No newline at end of file diff --git a/InstallerSamples/BlockFileOfT/СправкаData/main.html b/InstallerSamples/BlockFileOfT/СправкаData/main.html deleted file mode 100644 index 546c5940f..000000000 --- a/InstallerSamples/BlockFileOfT/СправкаData/main.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - - - - - - - - - <=
-
- Основной упор данной страницы - сравнение "file of T" и "BlockFileOf<T>".
- Но она также подходит и для обучения основам "BlockFileOf<T>". -
- - -

Объявление

- - - - -

Основы записи/чтения

- - - - -

Перечисление всех элементов в файле

- - - - -

Основное преимущество: сохранение записей (record)

- - - - - - \ No newline at end of file diff --git a/InstallerSamples/BlockFileOfT/Сравнение скорости.pas b/InstallerSamples/BlockFileOfT/Сравнение скорости.pas deleted file mode 100644 index a6c377d4d..000000000 --- a/InstallerSamples/BlockFileOfT/Сравнение скорости.pas +++ /dev/null @@ -1,289 +0,0 @@ -uses BlockFileOfT; - -// Эта процедура позволяет копировать память между содержимым массива и другим массивом/стеком -// При этом не волнуясь о блокировках массивов в памяти -// Если элемент массива передан как var-параметр, то сборщик мусора не трогает этот массив -procedure CopyMem(var o1: T1; var o2: T2; count: integer) := -System.Buffer.MemoryCopy( - @o1, @o2, - count, count -); - -type - StructLayout = System.Runtime.InteropServices.StructLayoutAttribute; - LayoutKind = System.Runtime.InteropServices.LayoutKind; - FieldOffset = System.Runtime.InteropServices.FieldOffsetAttribute; - - - - // Size контролирует размер записи. Его можно не указывать, тогда его выберет автоматически - // В нашем случае нужен один байт для поля length - // А затем место под 255 символов (масимум, который может хранить эта строка) - // При этом каждый символ занимает два байта в памяти - [StructLayout(LayoutKind.&Explicit, Size= 1 + 255*2 )] - ValueString255 = record - public [FieldOffset(0)] length: byte; - public [FieldOffset(1)] body: char; - - public const MaxLength = 255; - - // operator explicit, принимающий array of char и возвращающий ValueString255 выглядит в коде как: - // var s := ValueString255(a); - // Где тип у "a" — array of char - public static function operator explicit(a: array of char): ValueString255; - begin - Result.length := Min(MaxLength, a.Length); // если a.Length>MaxLength, то length надо обрезать до MaxLength - if Result.length=0 then exit; // иначе упадёт a[0] - CopyMem(a[0], Result.body, Result.length*2); // char занимает два байта, поэтому копируем length*2 - end; - - public static function operator explicit(s: string): ValueString255; - begin - Result.length := Min(MaxLength, s.Length); - if Result.length=0 then exit; - CopyMem(s[1], Result.body, Result.length*2); - end; - - public static function operator explicit(s: ValueString255): array of char; - begin - Result := new char[s.length]; - if s.length=0 then exit; - CopyMem(s.body, Result[0], s.length*2); - end; - - // объявлять тип CharArr может быть неудобно - public function ToCharArray: array of char; - type CharArr = array of char; - begin - Result := CharArr(self); - end; - - public static function operator explicit(s: ValueString255): string := - new string(@s.body, 0, s.length); - - public function ToString: string; override := - string(self); - - end; - - ///Это будет сохранять в file of T - AR = record - s: string[255]; - - //dt: DateTime; // Не даёт ¯\_(ツ)_/¯ - dt: int64; // Ну и ладно, в System.DateTime всё хранится в одном поле типа int64 - - i: integer; - ch: char; - b: byte; - end; - ///Это будет сохранять в BlockFileOf - BR = record - s: ValueString255; - dt: DateTime; // А BlockFileOf принимает любые размерные типы без ограничений - i: integer; - ch: char; - b: byte; - end; - - ///Это тип для ввода/вывода - ///В нём хранятся входные данные, общие и для file of T, и для BlockFileOf - ///Тут же описаны и преобразования всех особых типов - ///Заметьте, преобразование string[255] к string и назад ничего не копирует и не преобразовывает, если изначальная строка уже была <= 255 символов - ///Поэтому преобразования между ValueString255 и string работают медленнее - ///И даже при этом BlockFileOf всё равно быстрее - IOR = record - s: string; - dt: System.DateTime; - i: integer; - ch: char; - b: byte; - - static function operator explicit(a: IOR): AR; - begin - Result.s := a.s; - Result.dt := a.dt.Ticks; // Ticks возвращает никак не преобразованное значение единственного внутреннего поля DateTime - Result.i := a.i; - Result.ch := a.ch; - Result.b := a.b; - end; - - static function operator explicit(a: IOR): BR; - begin - Result.s := ValueString255(a.s); // вызываем наш operator explicit - Result.dt := a.dt; - Result.i := a.i; - Result.ch := a.ch; - Result.b := a.b; - end; - - static function operator explicit(a: AR): IOR; - begin - Result.s := a.s; - Result.dt := new DateTime(a.dt); // Единственный конструктор DateTime принимающий 1 параметр - принимает кол-во тиков. И напрямую присваивает это значение внутреннему полю - Result.i := a.i; - Result.ch := a.ch; - Result.b := a.b; - end; - - static function operator explicit(a: BR): IOR; - begin - Result.s := string(a.s); - Result.dt := a.dt; - Result.i := a.i; - Result.ch := a.ch; - Result.b := a.b; - end; - - static function GetRandom: IOR; - begin - Result.s := new string(ArrGen(Random(256), i -> ChrAnsi(Random(byte.MaxValue)))); - Result.dt := System.DateTime.Now.AddTicks(System.Convert.ToInt64(((Random * 2 - 1) * 1024 * 1024 * 1024 * 1024))); - Result.i := (Random(word.MaxValue) shl 16) + Random(word.MaxValue); - Result.ch := ChrAnsi(Random(byte.MaxValue)); - Result.b := Random(256); - end; - - public function ToString: string; override := - $'IOR( s.Length={s.Length}, dt={dt}, i={i}, ch="{ch}", b={b} )'; - - end; - -procedure TestIntegrity1(c: integer); -begin - - var f: file of AR; - var test_arr := ArrGen(c, i -> IOR.GetRandom); - - Rewrite(f, 'temp.bin'); - foreach var a in test_arr.Select(a -> AR(a)) do - f.Write(a); - - f.Close; - - Reset(f); - var n_test_arr := f.ReadElements.Select(a -> IOR(a)).ToArray; - f.Close; - - if not test_arr.SequenceEqual(n_test_arr) then - begin - writeln('тест 1 (file of T) не пройден'); - if test_arr.Length <> n_test_arr.Length then - writeln($'len: {test_arr.Length} <> {n_test_arr.Length}') else - for var i := 0 to test_arr.Length - 1 do - if test_arr[i] <> n_test_arr[i] then - begin - writeln($'el:{#10}{test_arr[i]}{#10}{n_test_arr[i]}{#10}'); - end; - readln; - exit; - end; - -end; - -procedure TestIntegrity2(c: integer); -begin - - var f := new BlockFileOf
('temp.bin'); - var test_arr := ArrGen(c, i -> IOR.GetRandom); - - f.Rewrite; - f.Write(test_arr.ConvertAll(a -> BR(a))); - f.Close; - - f.Reset; - var n_test_arr := f.Read(f.Size).ConvertAll(a -> IOR(a)); - f.Close; - - if not test_arr.SequenceEqual(n_test_arr) then - begin - writeln('тест 2 (BlockFileOf) не пройден'); - if test_arr.Length <> n_test_arr.Length then - writeln($'len: {test_arr.Length} <> {n_test_arr.Length}') else - for var i := 0 to test_arr.Length - 1 do - if test_arr[i] <> n_test_arr[i] then - begin - writeln($'el:{#10}{test_arr[i]}{#10}{n_test_arr[i]}{#10}'); - end; - - readln; - exit; - end; - -end; - -procedure TestIntegrity; -begin - TestIntegrity1(10000); - writeln('тест 1 ok'); - TestIntegrity2(10000); - writeln('тест 2 ok'); - writeln('тесты ok'); -end; - -procedure TestSpeed; -begin - - var sw := new System.Diagnostics.Stopwatch; // Точнее, чем этим, замерить невозможно - var lc := 10; - var ec := 10000;//Чем больше элементов, тем больше преимущество BlockFileOf, потому что он сохраняет их всех сразу. - //Но он быстрее даже если сохранять по одному элементу - var t1, t2: int64; - - var f1: file of AR; - Assign(f1, 'temp.bin'); - var f2 := new BlockFileOf
('temp.bin'); - - while true do - begin - - var test_arr := ArrGen(ec, i -> IOR.GetRandom); - - sw.Restart; - loop lc do - begin - Rewrite(f1); - var AR_arr := test_arr.ConvertAll(a -> AR(a)); - foreach var a in AR_arr do f1.Write(a); - f1.Close; - - Reset(f1); - ArrGen(ec, i -> IOR(f1.Read)); - f1.Close; - end; - sw.Stop; - t1 += sw.ElapsedTicks; - - sw.Restart; - loop lc do - begin - f2.Rewrite; - f2.Write(test_arr.ConvertAll(a -> BR(a))); - f2.Close; - - f2.Reset; - f2.Read(ec).ConvertAll(a -> IOR(a)); - f2.Close; - end; - sw.Stop; - t2 += sw.ElapsedTicks; - - System.GC.Collect; - System.Console.Clear; - writeln($'file of T: {t1}'); - writeln($'BlockFile: {t2}'); - end; - -end; - -begin - // Уберите флажок "Debug версия" в "Сервис>>Настройки>>Опции компиляции" и запускайте по Shift+F9, иначе отладка может неравномерно влиять на результаты - - // Тест на отсутствие ошибок. Можно убрать - TestIntegrity; - - // Тест скорости - // Он бесконечный, чем дольше тестируется - тем более усреднённые, а значит и более точные результаты - TestSpeed; - -end. \ No newline at end of file diff --git a/InstallerSamples/Experimental/1.pas b/InstallerSamples/Experimental/1.pas deleted file mode 100644 index 0519ecba6..000000000 --- a/InstallerSamples/Experimental/1.pas +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/InstallerSamples/Other/SpeedTests/ListVsArray.pas b/InstallerSamples/Other/SpeedTests/ListVsArray.pas new file mode 100644 index 000000000..70873a174 --- /dev/null +++ b/InstallerSamples/Other/SpeedTests/ListVsArray.pas @@ -0,0 +1,12 @@ +begin + var n := 300000000; + var a := ArrFill(n,1); + var l := Lst(a); + MillisecondsDelta; + for var i:=0 to n-1 do + a[i] := 2; + MillisecondsDelta.Println; // 143 ms + for var i:=0 to n-1 do + l[i] := 2; + MillisecondsDelta.Println; // 357 ms +end. \ No newline at end of file diff --git a/InstallerSamples/Other/SpeedTests/string_byref_byvalue.pas b/InstallerSamples/Other/SpeedTests/string_byref_byvalue.pas new file mode 100644 index 000000000..16d4fa91d --- /dev/null +++ b/InstallerSamples/Other/SpeedTests/string_byref_byvalue.pas @@ -0,0 +1,25 @@ +var ss:int64 := 0; + +procedure p(s: string); +begin + s[1] := '1'; +end; + +procedure q(var s: string); +begin + s[1] := '1'; +end; + +begin + var n := 100000000; + var s := 'a'*n; + MillisecondsDelta; + loop 10 do + p(s); + Println(s[:3]); + MillisecondsDelta.Println; + loop 10 do + q(s); + Println(s[:3]); + MillisecondsDelta.Println; +end. \ No newline at end of file diff --git a/Release/pabcversion.txt b/Release/pabcversion.txt index 56ecb546d..d7afe3f18 100644 --- a/Release/pabcversion.txt +++ b/Release/pabcversion.txt @@ -1 +1 @@ -3.8.2.3059 +3.8.2.3061 diff --git a/ReleaseGenerators/PascalABCNET_version.nsh b/ReleaseGenerators/PascalABCNET_version.nsh index c6b1c0251..18c139805 100644 --- a/ReleaseGenerators/PascalABCNET_version.nsh +++ b/ReleaseGenerators/PascalABCNET_version.nsh @@ -1 +1 @@ -!define VERSION '3.8.2.3059' +!define VERSION '3.8.2.3061' diff --git a/TreeConverter/SystemLib/StaticSystemLib.cs b/TreeConverter/SystemLib/StaticSystemLib.cs index fd5ec5b47..bd3e217e8 100644 --- a/TreeConverter/SystemLib/StaticSystemLib.cs +++ b/TreeConverter/SystemLib/StaticSystemLib.cs @@ -404,6 +404,15 @@ namespace PascalABCCompiler.SystemLibrary private static basic_function_node _ulong_shr; //float + private static basic_function_node _float_to_byte; + private static basic_function_node _float_to_sbyte; + private static basic_function_node _float_to_short; + private static basic_function_node _float_to_ushort; + private static basic_function_node _float_to_int; + private static basic_function_node _float_to_uint; + private static basic_function_node _float_to_long; + private static basic_function_node _float_to_ulong; + private static basic_function_node _float_to_double; private static basic_function_node _float_assign; private static basic_function_node _float_unmin; @@ -1801,10 +1810,27 @@ namespace PascalABCCompiler.SystemLibrary _double_to_int = make_type_conversion(_ulong_type, _integer_type, type_compare.non_comparable_type, SemanticTree.basic_function_type.ultoi); _double_to_char = make_type_conversion(_ulong_type, _char_type, type_compare.greater_type, SemanticTree.basic_function_type.ultochar); */ - _double_to_int = make_type_conversion(_double_type, _integer_type, type_compare.greater_type, SemanticTree.basic_function_type.dtoi,false); + _float_to_sbyte = make_type_conversion(_float_type, _sbyte_type, type_compare.greater_type, SemanticTree.basic_function_type.ftosb, false); + _float_to_byte = make_type_conversion(_float_type, _byte_type, type_compare.greater_type, SemanticTree.basic_function_type.ftob, false); + _float_to_ushort = make_type_conversion(_float_type, _ushort_type, type_compare.greater_type, SemanticTree.basic_function_type.ftous, false); + _float_to_short = make_type_conversion(_float_type, _short_type, type_compare.greater_type, SemanticTree.basic_function_type.ftos, false); + _float_to_ulong = make_type_conversion(_float_type, _uint64_type, type_compare.greater_type, SemanticTree.basic_function_type.ftoul, false); + _float_to_long = make_type_conversion(_float_type, _int64_type, type_compare.greater_type, SemanticTree.basic_function_type.ftol, false); + _float_to_uint = make_type_conversion(_float_type, _uint_type, type_compare.greater_type, SemanticTree.basic_function_type.ftoui, false); + _float_to_int = make_type_conversion(_float_type, _integer_type, type_compare.greater_type, SemanticTree.basic_function_type.ftoi, false); + + + _double_to_sbyte = make_type_conversion(_double_type, _sbyte_type, type_compare.greater_type, SemanticTree.basic_function_type.dtosb, false); + _double_to_byte = make_type_conversion(_double_type, _byte_type, type_compare.greater_type, SemanticTree.basic_function_type.dtob, false); + _double_to_ushort = make_type_conversion(_double_type, _ushort_type, type_compare.greater_type, SemanticTree.basic_function_type.dtous, false); + _double_to_short = make_type_conversion(_double_type, _short_type, type_compare.greater_type, SemanticTree.basic_function_type.dtos, false); + _double_to_ulong = make_type_conversion(_double_type, _uint64_type, type_compare.greater_type, SemanticTree.basic_function_type.dtoul, false); + _double_to_long = make_type_conversion(_double_type, _int64_type, type_compare.greater_type, SemanticTree.basic_function_type.dtol, false); + _double_to_uint = make_type_conversion(_double_type, _uint_type, type_compare.greater_type, SemanticTree.basic_function_type.dtoui, false); + _double_to_int = make_type_conversion(_double_type, _integer_type, type_compare.greater_type, SemanticTree.basic_function_type.dtoi, false); _double_to_float = make_type_conversion(_double_type, _float_type, type_compare.greater_type, SemanticTree.basic_function_type.dtof); //Унарные операции. - _real_unmin=make_unary_operator(compiler_string_consts.minus_name, _double_type, SemanticTree.basic_function_type.dunmin); + _real_unmin = make_unary_operator(compiler_string_consts.minus_name, _double_type, SemanticTree.basic_function_type.dunmin); //make_empty_operator(compiler_string_consts.plus_name, _real_type); make_unary_empty_operator(compiler_string_consts.plus_name, _double_type, _double_type); //Арифметические операции.