float to int explicitly
Move some samples to Experimental
This commit is contained in:
parent
775b363a45
commit
7e472fa16f
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%MINOR%=8
|
||||
%REVISION%=3059
|
||||
%REVISION%=3061
|
||||
%COREVERSION%=2
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
///Модуль с массивом, хранящим данные в файле
|
||||
unit FileArray;
|
||||
|
||||
interface
|
||||
|
||||
uses BlockFileOfT;
|
||||
|
||||
type
|
||||
/// Массив, хранящий данные в файле
|
||||
/// Намного медленнее обычного массива, однако может хранить бОльший объём данных
|
||||
/// T может быть только записью, не содержащей ссылочные поля (указатели, классы и т.п.)
|
||||
FileArr<T> = sealed class
|
||||
where T: record;
|
||||
|
||||
private f: BlockFileOf<T>;
|
||||
///Автоматически очищает кэш после каждого чтения и записи
|
||||
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<T> 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<T>(fname);
|
||||
f.Open(mode);
|
||||
end;
|
||||
|
||||
///Использует заданный файл как основу для массива
|
||||
///Если файл не открыт - его откроет в режиме System.IO.FileMode.OpenOrCreate
|
||||
public constructor(&file: BlockFileOf<T>);
|
||||
begin
|
||||
f := &file;
|
||||
if not f.Opened then
|
||||
f.Open(System.IO.FileMode.OpenOrCreate);
|
||||
end;
|
||||
|
||||
///Открывает заданный файл в режиме mode и использует его как основу для массива
|
||||
public constructor(&file: BlockFileOf<T>; 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<T>.GetItem32(i: integer) := GetItem64(i);
|
||||
|
||||
function FileArr<T>.GetSection32(from, c: integer) := GetSection64(from, c);
|
||||
|
||||
function FileArr<T>.GetSectionLazy32(from, c: integer) := GetSectionLazy64(from, c);
|
||||
|
||||
procedure FileArr<T>.SetItem32(i: integer; o: T) := SetItem64(i, o);
|
||||
|
||||
procedure FileArr<T>.SetSection32(from, c: integer; o: array of T) := SetSection64(from, c, o);
|
||||
|
||||
procedure FileArr<T>.SetSectionLazy32(from, c: integer; o: sequence of T) := SetSectionLazy64(from, c, o);
|
||||
|
||||
function FileArr<T>.GetItem64(i: int64): T;
|
||||
begin
|
||||
f.Seek(i);
|
||||
Result := f.Read;
|
||||
if AutoFlush then f.BaseStream.Flush;
|
||||
end;
|
||||
|
||||
function FileArr<T>.GetSection64(from, c: int64): array of T;
|
||||
begin
|
||||
f.Seek(from);
|
||||
Result := f.Read(c);
|
||||
if AutoFlush then f.BaseStream.Flush;
|
||||
end;
|
||||
|
||||
function FileArr<T>.GetSectionLazy64(from, c: int64): sequence of T;
|
||||
begin
|
||||
f.Seek(from);
|
||||
Result := f.ReadLazy(c);
|
||||
if AutoFlush then f.BaseStream.Flush;
|
||||
end;
|
||||
|
||||
procedure FileArr<T>.SetItem64(i: int64; o: T);
|
||||
begin
|
||||
f.Seek(i);
|
||||
f.Write(o);
|
||||
if AutoFlush then f.BaseStream.Flush;
|
||||
end;
|
||||
|
||||
procedure FileArr<T>.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<T>.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.
|
||||
|
|
@ -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<DataUnit>;
|
||||
public Data: FileArr<DataUnit>;
|
||||
|
||||
public constructor := exit;
|
||||
|
||||
public constructor(fname: string; version: byte; DatabaseType: string);
|
||||
begin
|
||||
self.version := version;
|
||||
self.DatabaseType := DatabaseType;
|
||||
|
||||
self.BlockFile := new BlockFileOf<DataUnit>(fname);
|
||||
self.BlockFile.Offset := HeaderSize;
|
||||
self.BlockFile.Open(System.IO.FileMode.Create);
|
||||
|
||||
self.Data := new FileArr<DataUnit>(self.BlockFile);
|
||||
end;
|
||||
|
||||
public class function Load(fname: string): Database;
|
||||
begin
|
||||
Result := new Database;
|
||||
|
||||
Result.BlockFile := new BlockFileOf<DataUnit>(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<DataUnit>(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.
|
||||
|
|
@ -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<r1>('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<r2>;
|
||||
// f2.TSize это то же самое, что sizeof(r2)
|
||||
// У меня 16, хотя sizeof(byte)=1 + sizeof(int64)=8, то есть должно быть 9?
|
||||
// Это потому, что на многих процессорах добавляет отступы (так легче читать/записывать значения) в некоторые записи, так что будьте осторожны
|
||||
// Это также хорошая причина не пользоваться бездумно BaseStream - потому что на разных компьютерах один и тот же код может работать по-разному
|
||||
Writeln(f2.TSize);
|
||||
|
||||
var f3 := new BlockFileOf<r3>;
|
||||
// А вот теперь размер 9, потому что мы явно указали что отступ нам не нужен
|
||||
// Конечно, теперь чтение/запись i может быть медленнее на некоторых процессорах, отступ всё же не для красоты был
|
||||
Writeln(f3.TSize);
|
||||
|
||||
end.
|
||||
|
|
@ -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<r1>;
|
||||
// f := new BlockFileOf<r1>; // В отличии от file of T - BlockFileOf<T> всегда надо инициализировать перед тем как использовать
|
||||
// f.Assign('temp.bin');
|
||||
var f := new BlockFileOf<r1>('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<T> нету режима чтения и режима записи
|
||||
// Они объеденены, поэтому не надо закрывать файл чтобы записать что-то после чтения
|
||||
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.
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style type="text/css">
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
<font size="3">Выберете раздел:</font>
|
||||
<ul>
|
||||
<li><font size="4"><a href="СправкаData/main.html">Основы</a></font></li>
|
||||
<li><font size="4"><a href="СправкаData/WhyOffset.html">Для чего нужен Offset</a></font></li>
|
||||
<li><font size="4"><a href="СправкаData/RefTSaving.html">Сохранение ссылочных типов</a></font></li>
|
||||
</ul>
|
||||
<br>
|
||||
Если найдёте что-то неправильное или ошибку (в том числе и грамматическую) в справке - пишите <a href=https://github.com/SunSerega/PascalABC.Net-BlockFileOfT/issues>сюда</a>.
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style type="text/css">
|
||||
.code_block {
|
||||
background: #c0c0c0;
|
||||
padding: 5px;
|
||||
padding-right: 20px;
|
||||
border: solid 1px black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
function toggle_show(id) {
|
||||
document.getElementById(id).style.display = document.getElementById(id).style.display == 'none' ? 'block' : 'none';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
<font size="3"><a href="../Справка.html"><=</a></font><br>
|
||||
<br>
|
||||
<font size="5">Сохранение ссылочных типов в блочный файл.</font><br>
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
<p onClick="toggle_show('decl')"><font size="4" color="008000" style="cursor: pointer">В чём проблема ссылочных типов</font></p>
|
||||
<div id=decl style="display: none">
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">type </t>
|
||||
<br><t style="margin-left: 10px">r1 = record </t>
|
||||
<br><t style="margin-left: 20px">b: byte; </t>
|
||||
<br><t style="margin-left: 20px">i: integer; </t>
|
||||
<br><t style="margin-left: 20px">s: string; </t>
|
||||
<br><t style="margin-left: 10px">end; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var a: r1; </t>
|
||||
<br><t style="margin-left: 10px">a.b := 5; </t>
|
||||
<br><t style="margin-left: 10px">a.i := 12345; </t>
|
||||
<br><t style="margin-left: 10px">a.s := 'abcdef'*10000;//Это 60 000 символов, ~120KB. А значит в записи никаким образом не поместилось бы </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
r1, byte и integer из этого примера - размерные типы (записи), а значит они хранят свои данные прямо в переменных.<br>
|
||||
А string - ссылочный тип (класс). Это значит что в поле r1.s хранится только ссылка (особый указатель) на содержимое строки.<br>
|
||||
<br>
|
||||
То есть, в переменной "a" хранится три значения: byte, integer и ссылка.<br>
|
||||
А содержимое строки хранится где-то в оперативной памяти, куда указывает эта ссылка.<br>
|
||||
<br>
|
||||
"BlockFileOf<T>" берёт содержимое переменных одним блоком памяти.<br>
|
||||
Поэтому, если попытаться сохранить содержимое r1 в типизированный файл.<br>
|
||||
(отключив защиту от дурака, запрещающую ссылочные типы)<br>
|
||||
В файл сохранится значение ссылки, а не строки.<br>
|
||||
<br>
|
||||
Кроме того, ссылки отличаются от указателей тем, что они ссылаются на значение, которое может "плавать" в оперативной памяти.<br>
|
||||
Точнее, сборщик мусора может двигать эти значения, для экономии места.<br>
|
||||
Это значит что если сохранить запись с ссылкой в файл и сразу загрузить эту ссылку назад.<br>
|
||||
Её значение может быть уже устаревшим.<br>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<p onClick="toggle_show('io_basics')"><font size="4" color="008000" style="cursor: pointer">Размерные массивы и строки</font></p>
|
||||
<div id=io_basics style="display: none">
|
||||
|
||||
Посмотрите в этот файл:<br>
|
||||
C:\PABCWork.NET\Samples\BlockFileOfT\Сравнение скорости.pas<br>
|
||||
Найдите там тип "ValueString255".<br>
|
||||
На нём будут строиться дальнейшие обноснования:<br>
|
||||
Этот тип представляет строку с фиксированной длинной, а точнее длиной в 255 символов.<br>
|
||||
<br>
|
||||
Первое, что видно - прямо перед объявлением типа стоит атрибут "StructLayout":<br>
|
||||
"LayoutKind.Explicit" разрешает применять атрибут "FieldOffset" к полям.<br>
|
||||
А "Size" позволяет явно указать сколько байт выделять под данную запись.<br>
|
||||
Благодаря последнему - можно выделить место сразу под все 255 символов строки, не делая 255 полей с типом "char".<br>
|
||||
<br>
|
||||
Далее, в записи "ValueString255" есть всего 2 поля, "length" (длина) и "body" (тело, а точнее его начало)<br>
|
||||
"length" это кол-во символов во всей строке. В данном случае не может быть больше 255 символов,<br>
|
||||
поэтому достаточно 1 байта чтобы хранить любую возможную длину строки.<br>
|
||||
А "body" это начало самогО содержимого строки.<br>
|
||||
Вообще, поле "body" не так уж и нужно, но с ним будет проще, потому что если создать указатель на "body" -<br>
|
||||
он сразу оказывается и указателем на содержимое строки, без каких-либо преобразований.<br>
|
||||
<br>
|
||||
Далее идут методы для преобразования между обычной строкой и нашей размерной.<br>
|
||||
Я сделал их "operator explicit"-ами, потому что их вызов красивый и удобный.<br>
|
||||
<br>
|
||||
Смотрим в первый, преобразовывающий массив символов в размерную строку:<br>
|
||||
<br>
|
||||
Если выполнить "Result.length := a.Length" и передать массив длинной больше 255 символов -<br>
|
||||
в этой строчке произойдёт переполнение и "Result.length" запонится мусором, поэтому сначала надо уменьшить значение до "MaxLength".<br>
|
||||
<br>
|
||||
Далее, если длина равна нулю - ничего больше делать не надо.<br>
|
||||
В противном случае - используем процедуру "CopyMem".<br>
|
||||
<br>
|
||||
Насчёт того, как работает "CopyMem", можно написать целую диссертацию.<br>
|
||||
Но главное, что надо знать вам: если попытаться получить указатель на элемент массива ("@a[n]") -<br>
|
||||
получаем утечку памяти. Однако, если передать "a[n]" в "CopyMem" как var-параметр<br>
|
||||
(что, по сути, тоже создаёт указатель на этот элемент массива),<br>
|
||||
то всё в порядке.<br>
|
||||
<br>
|
||||
В данном случае мы копируем память начиная с нулевого элемента массива<br>
|
||||
Вставляем её в "Result.body"<br>
|
||||
И всего копируем "Result.length*2" байт (*2 потому что каждый символ занимает 2 байта в памяти)<br>
|
||||
<br>
|
||||
Преобразование из "string" в размерную строку работает также, там ничего интересного.<br>
|
||||
У преобразования размерной строки в массив символов всего одна особенность:<br>
|
||||
Теперь результат у нас ссылочный (массив), поэтому память под него нужно выделить вручную ("new char[...]").<br>
|
||||
<br>
|
||||
И последнее преобразование, из "ValueString255" в "string":<br>
|
||||
А вот тут очень приятно вышло - у "string" есть конструктор который идеально подходит:<br>
|
||||
Первый параметр это указатель на начало блока памяти содержащего символы. То есть "@s.body" и даже тип подходит без преобразований.<br>
|
||||
Второй параметр - смещение начала. У нас символы начинаются прямо на "body", поэтому 0.<br>
|
||||
И последний - количество символов.<br>
|
||||
<br>
|
||||
Последнее, что есть в данной записи - перегрузка "ToString". Это вообще не обязательный код,<br>
|
||||
но с этой функцией размерная строка будет вести себя точно так же, как и обычная, если передать её во "Writeln".<br>
|
||||
Также можно добавлять и другие свои методы в данный тип и даже сделать все те же методы, как и у ссылочных строк, или даже больше.<br>
|
||||
<br>
|
||||
Ну а размерные массивы ничем не отличаются в реализации.<br>
|
||||
Можно даже сказать, что "ValueString255" это размерный массив символов, ничего от этого не изменится.<br>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style type="text/css">
|
||||
.code_block {
|
||||
background: #c0c0c0;
|
||||
padding: 5px;
|
||||
padding-right: 20px;
|
||||
border: solid 1px black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
function toggle_show(id) {
|
||||
document.getElementById(id).style.display = document.getElementById(id).style.display == 'none' ? 'block' : 'none';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
<font size="3"><a href="../Справка.html"><=</a></font><br>
|
||||
<br>
|
||||
<hr />
|
||||
|
||||
|
||||
<p onClick="toggle_show('io_basics')"><font size="4" color="008000" style="cursor: pointer">Что такое "Offset"</font></p>
|
||||
<div id=io_basics style="display: none">
|
||||
"Offset" (он же отступ) — кол-во байт в начале файла, которые не будут использоваться под элементы, сохранённые в файл.<br>
|
||||
При этом, эту область можно использовать под что угодно другое.<br>
|
||||
Чаще всего — под заголовок файла, описывающий какую-то общую для всех элементов файла информацию.<br>
|
||||
<br>
|
||||
Если у файла есть отступ — "Size" и "Pos" будут учитывать его.<br>
|
||||
То есть, если длина файла 11 байт, один элемент занимает 2 байт, а отступ 3 байт - "Size" будет возвращать 4, потому что (11-3)/2 = 4.<br>
|
||||
<br>
|
||||
Но, что очень важно помнить: когда файл только открыт/создан любым способом, кроме "Append" -<br>
|
||||
позиция в файле находится в самом начале, то есть на отступе.<br>
|
||||
А значит, операции чтения и записи будут начинать работать с самого начала файла, а не с конца отступа.<br>
|
||||
Поэтому, в большинстве случаев лучше вызвать "f.Pos := 0" перед чтением/записью.<br>
|
||||
Это поставит позицию в файле на элемент #0, то есть сразу после отступа.
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<p onClick="toggle_show('decl')"><font size="4" color="008000" style="cursor: pointer">Заголовки</font></p>
|
||||
<div id=decl style="display: none">
|
||||
И так, как написано выше, основное применение - заголовок файла:
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">uses BlockFileOfT; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">type </t>
|
||||
<br><t style="margin-left: 10px">HeaderT = record </t>
|
||||
<br><t style="margin-left: 20px">version: integer; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 20px">public function ToString: string; override := </t>
|
||||
<br><t style="margin-left: 20px">$'Header: [version={version}]'; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">end; </t>
|
||||
<br><t style="margin-left: 10px">ElementT = byte; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f := new BlockFileOf<ElementT>('temp.bin',sizeof(HeaderT)); // Этот конструктор установит Offset на sizeof(HeaderT) </t>
|
||||
<br><t style="margin-left: 10px">f.Rewrite; </t>
|
||||
<br><t style="margin-left: 10px">begin </t>
|
||||
<br><t style="margin-left: 20px">var hf := new BlockFileOf<HeaderT>(f); // Этот конструктор создаст новый "BlockFileOf<T>", у которого будет общий фаловый поток с "f" </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 20px">var h: HeaderT; </t>
|
||||
<br><t style="margin-left: 20px">h.version := 5; </t>
|
||||
<br><t style="margin-left: 20px">hf.Write(h); </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 20px">// Эта строчка не закроет поток, потому что файл сейчас открыт в двух переменных </t>
|
||||
<br><t style="margin-left: 20px">// Но она отвяжет "hf" от файла открытого в "f", чтобы когда "f" закрылся - файл тоже был закрыт </t>
|
||||
<br><t style="margin-left: 20px">hf.Close; </t>
|
||||
<br><t style="margin-left: 10px">end; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Write( </t>
|
||||
<br><t style="margin-left: 20px">1,2,3,4,5,6,7,8,9 </t>
|
||||
<br><t style="margin-left: 10px">); </t>
|
||||
<br><t style="margin-left: 10px">f.Close; // А вот теперь поток будет закрыт и файл сохранён, потому что теперь это последняя перменная, в которой открыт файл </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Reset; </t>
|
||||
<br><t style="margin-left: 10px">begin </t>
|
||||
<br><t style="margin-left: 20px">var hf := new BlockFileOf<HeaderT>(f); </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 20px">var h := hf.Read; </t>
|
||||
<br><t style="margin-left: 20px">writeln(h); </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 20px">hf.Close;//Опять же, надо закрыть сразу, как закончили пользоваться </t>
|
||||
<br><t style="margin-left: 10px">end; </t>
|
||||
<br><t style="margin-left: 10px">f.Read(9).Print; </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
Прошу обратить внимание на то, что я не поставил тут "f.Pos := 0" перед чтением/записью только по стечению двух обстоятельств:<br>
|
||||
1. Этот код линейный, в нём хорошо видно, что прямо перед чтением/записью элементов идёт чтение/запись заголовка. А значит, файловый курсор сам передвинется на sizeof(HeaderT).<br>
|
||||
2. Написав этот короткий участок кода я протестировал его, чтобы быть уверенным в том, что я ничего не забыл и не перепутал.
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,265 +0,0 @@
|
|||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style type="text/css">
|
||||
.code_block {
|
||||
background: #c0c0c0;
|
||||
padding: 5px;
|
||||
padding-right: 20px;
|
||||
border: solid 1px black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
function toggle_show(id) {
|
||||
document.getElementById(id).style.display = document.getElementById(id).style.display == 'none' ? 'block' : 'none';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
<font size="3"><a href="../Справка.html"><=</a></font><br>
|
||||
<br>
|
||||
Основной упор данной страницы - сравнение "file of T" и "BlockFileOf<T>".<br>
|
||||
Но она также подходит и для обучения основам "BlockFileOf<T>".
|
||||
<hr />
|
||||
|
||||
|
||||
<p onClick="toggle_show('decl')"><font size="4" color="008000" style="cursor: pointer">Объявление</font></p>
|
||||
<div id=decl style="display: none">
|
||||
<font size="3">"file of T":</font>
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f: file of T; </t>
|
||||
<br><t style="margin-left: 10px">Assign(f, 'temp.bin'); </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
<font size="3">"BlockFileOf<T>":</font>
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">uses BlockFileOfT; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f: BlockFileOf<T>; </t>
|
||||
<br><t style="margin-left: 10px">f := new BlockFileOf<T>; // В отличии от file of T - BlockFileOf<T> нужно всегда инициализировать </t>
|
||||
<br><t style="margin-left: 10px">f.Assign('temp.bin'); </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
<font size="3">Последнее можно сократить:</font>
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">uses BlockFileOfT; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f := new BlockFileOf<T>; </t>
|
||||
<br><t style="margin-left: 10px">f.Assign('temp.bin'); </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
<font size="3">Или ещё больше сократить:</font>
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">uses BlockFileOfT; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f := new BlockFileOf<byte>('temp.bin'); </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<p onClick="toggle_show('io_basics')"><font size="4" color="008000" style="cursor: pointer">Основы записи/чтения</font></p>
|
||||
<div id=io_basics style="display: none">
|
||||
<font size="3">"file of T":</font>
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f: file of integer; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">Rewrite(f, 'temp.bin'); </t>
|
||||
<br><t style="margin-left: 10px">f.Write( </t>
|
||||
<br><t style="margin-left: 20px">1,2,3,4,5 </t>
|
||||
<br><t style="margin-left: 10px">); </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">Reset(f); </t>
|
||||
<br><t style="margin-left: 10px">loop 5 do </t>
|
||||
<br><t style="margin-left: 20px">f.Read.Print; </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
<font size="3">"BlockFileOf<T>":</font>
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">uses BlockFileOfT; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f := new BlockFileOf<integer>('temp.bin'); </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Rewrite; </t>
|
||||
<br><t style="margin-left: 10px">f.Write( </t>
|
||||
<br><t style="margin-left: 20px">1,2,3,4,5 </t>
|
||||
<br><t style="margin-left: 10px">); </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Reset; </t>
|
||||
<br><t style="margin-left: 10px">f.Read(5).Print; </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
В случае с "BlockFileOf<T>" все 5 элементов читаются одним блоком памяти.<br>
|
||||
В то время как "file of T" читает по одному элементу.<br>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<p onClick="toggle_show('to_seq')"><font size="4" color="008000" style="cursor: pointer">Перечисление всех элементов в файле</font></p>
|
||||
<div id=to_seq style="display: none">
|
||||
<font size="3">"file of T":</font>
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f: file of integer; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">Rewrite(f, 'temp.bin'); </t>
|
||||
<br><t style="margin-left: 10px">f.Write( </t>
|
||||
<br><t style="margin-left: 20px">1,2,3,4,5 </t>
|
||||
<br><t style="margin-left: 10px">); </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">Reset(f); </t>
|
||||
<br><t style="margin-left: 10px">f.ReadElements.Print </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
<font size="3">"BlockFileOf<T>":</font>
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">uses BlockFileOfT; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f := new BlockFileOf<integer>('temp.bin'); </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Rewrite; </t>
|
||||
<br><t style="margin-left: 10px">f.Write( </t>
|
||||
<br><t style="margin-left: 20px">1,2,3,4,5 </t>
|
||||
<br><t style="margin-left: 10px">); </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Reset; </t>
|
||||
<br><t style="margin-left: 10px">f.ToSeq.Print; </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
"BlockFileOf<T>.ToSeq" работает так же, как и "(file of T).ReadElements" (если не считать более продвинутой защиты от дурака):<br>
|
||||
Они оба читают файл по одному элементу и после каждого из них проверяют, не достигнут ли конец файла.<br>
|
||||
<br>
|
||||
Но в "BlockFileOf<T>" есть альтернатива - "ToSeqBlocks".<br>
|
||||
Эта функция читает всё из файла блоками заданной длины в байтах. Если не задавать длину, данные будут читаться блоками по 4 килобайта.<br>
|
||||
В нашем случае, "integer" занимает 4 байта в памяти, значит в каждом блоке по 4 КБ поместится 1024 элемента типа "integer"<br>
|
||||
(Объём типа T можно узнать из свойства "BlockFileOf<T>.TSize", или с помощью sizeof(T) )<br>
|
||||
В случае с пятью элементами это едва ли даст преимущество, но если записывать и считывать сразу большое количество элементов - считывание блоками будет намного быстрее:
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">uses BlockFileOfT; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f := new BlockFileOf<integer>('temp.bin'); </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Rewrite; </t>
|
||||
<br><t style="margin-left: 10px">f.Write( </t>
|
||||
<br><t style="margin-left: 20px">1,2,3,4,5 </t>
|
||||
<br><t style="margin-left: 10px">); </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Reset; </t>
|
||||
<br><t style="margin-left: 10px">var blocks := f.ToSeqBlocks; </t>
|
||||
<br><t style="margin-left: 10px">var AsSeq := blocks.SelectMany(bl->bl); </t>
|
||||
<br><t style="margin-left: 10px">AsSeq.Print; </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
</p>
|
||||
"AsSeq := blocks.SelectMany(bl->bl)" здесь делает так, что хоть всё считывается из файла блоками,<br>
|
||||
переменную "AsSeq" будет воспринимать как последовательность элементов, а не последовательность блоков.<br>
|
||||
И - я расписал вывод как 3 строчки чтоб было виднее. Можно и сократить:
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 10px">f.Reset; </t>
|
||||
<br><t style="margin-left: 10px">f.ToSeqBlocks.SelectMany(bl->bl).Print; </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
</p>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<p onClick="toggle_show('bfo_record')"><font size="4" color="008000" style="cursor: pointer">Основное преимущество: сохранение записей (record)</font></p>
|
||||
<div id=bfo_record style="display: none">
|
||||
<p class=code_block>
|
||||
<t style="margin-left: 00px">uses BlockFileOfT; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">type </t>
|
||||
<br><t style="margin-left: 10px">r1 = record </t>
|
||||
<br><t style="margin-left: 20px">b1: byte; </t>
|
||||
<br><t style="margin-left: 20px">i: integer; </t>
|
||||
<br><t style="margin-left: 20px">b2: byte; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 20px">constructor(b1: byte; i: integer; b2: byte); </t>
|
||||
<br><t style="margin-left: 20px">begin </t>
|
||||
<br><t style="margin-left: 30px">self.b1 := b1; </t>
|
||||
<br><t style="margin-left: 30px">self.i := i; </t>
|
||||
<br><t style="margin-left: 30px">self.b2 := b2; </t>
|
||||
<br><t style="margin-left: 20px">end; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 20px">///Эта функция определяет то, как данную запись выведет на экран </t>
|
||||
<br><t style="margin-left: 20px">function ToString: string; override := </t>
|
||||
<br><t style="margin-left: 20px">$'r1(b1={b1}, i={i}, b2={b2})'; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">end; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">begin </t>
|
||||
<br><t style="margin-left: 10px">var f := new BlockFileOf<r1>('temp.bin'); </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Rewrite; </t>
|
||||
<br><t style="margin-left: 10px">f.Write( </t>
|
||||
<br><t style="margin-left: 20px">new r1(1,2,3) </t>
|
||||
<br><t style="margin-left: 10px">); </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 10px">f.Reset; </t>
|
||||
<br><t style="margin-left: 10px">writeln(f.Read); </t>
|
||||
<br><t style="margin-left: 10px">f.Close; </t>
|
||||
<br><t style="margin-left: 00px"> </t>
|
||||
<br><t style="margin-left: 00px">end. </t>
|
||||
|
||||
</p>
|
||||
Казалось бы, тот же самый код будет работать и для "file of T".<br>
|
||||
Но вся разница находится в реализации:<br>
|
||||
<br>
|
||||
"file of T" в данном случае при вызове "Read" сначала прочитал бы названия и типы полей "r1" (через "System.Reflection"),<br>
|
||||
а затем, по очереди, читал бы из файла значение того же типа, что и у поля ("byte", затем "integer" и снова "byte"),<br>
|
||||
сохраняя прочитаные значения в соответствующих полях результата.<br>
|
||||
<br>
|
||||
А "BlockFileOf<T>" подходит к этому с другой стороны.<br>
|
||||
Когда вы первый раз инициализируете переменную типа "BlockFileOf<r1>" -<br>
|
||||
объём в памяти, занимаемый переменными типа "r1", вычисляется и сохраняется во внутреннее статическое поле<br>
|
||||
(Его можно получить через свойство "TSize")<br>
|
||||
И когда вызывается функция "Read" - "BlockFileOf<T>" читает из файла столько байт, сколько занимают переменные типа "r1",<br>
|
||||
А потом копирует содержимое полученного массива байт в результат.<br>
|
||||
<br>
|
||||
То есть, "file of T" читает три блока, а "BlockFileOf<T>" только один.<br>
|
||||
И когда объём данных одинаковый - количество блоков очень важный фактор.<br>
|
||||
Поэтому "BlockFileOf<T>" будет быстрее.<br>
|
||||
И размер одного блока не ограничивается размером одной переменной типа "r1".<br>
|
||||
"Read(10)", к примеру, прочитает одним блоком то, что "file of T" читал бы как 30 блоков.<br>
|
||||
<br>
|
||||
Ну и, конечно, всё то же самое касается и "Write".<br>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,289 +0,0 @@
|
|||
uses BlockFileOfT;
|
||||
|
||||
// Эта процедура позволяет копировать память между содержимым массива и другим массивом/стеком
|
||||
// При этом не волнуясь о блокировках массивов в памяти
|
||||
// Если элемент массива передан как var-параметр, то сборщик мусора не трогает этот массив
|
||||
procedure CopyMem<T1,T2>(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<T>
|
||||
BR = record
|
||||
s: ValueString255;
|
||||
dt: DateTime; // А BlockFileOf<T> принимает любые размерные типы без ограничений
|
||||
i: integer;
|
||||
ch: char;
|
||||
b: byte;
|
||||
end;
|
||||
|
||||
///Это тип для ввода/вывода
|
||||
///В нём хранятся входные данные, общие и для file of T, и для BlockFileOf<T>
|
||||
///Тут же описаны и преобразования всех особых типов
|
||||
///Заметьте, преобразование string[255] к string и назад ничего не копирует и не преобразовывает, если изначальная строка уже была <= 255 символов
|
||||
///Поэтому преобразования между ValueString255 и string работают медленнее
|
||||
///И даже при этом BlockFileOf<T> всё равно быстрее
|
||||
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<BR>('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<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 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<T>, потому что он сохраняет их всех сразу.
|
||||
//Но он быстрее даже если сохранять по одному элементу
|
||||
var t1, t2: int64;
|
||||
|
||||
var f1: file of AR;
|
||||
Assign(f1, 'temp.bin');
|
||||
var f2 := new BlockFileOf<BR>('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.
|
||||
|
|
@ -1 +0,0 @@
|
|||
|
||||
12
InstallerSamples/Other/SpeedTests/ListVsArray.pas
Normal file
12
InstallerSamples/Other/SpeedTests/ListVsArray.pas
Normal file
|
|
@ -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.
|
||||
25
InstallerSamples/Other/SpeedTests/string_byref_byvalue.pas
Normal file
25
InstallerSamples/Other/SpeedTests/string_byref_byvalue.pas
Normal file
|
|
@ -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.
|
||||
|
|
@ -1 +1 @@
|
|||
3.8.2.3059
|
||||
3.8.2.3061
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.8.2.3059'
|
||||
!define VERSION '3.8.2.3061'
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
//Арифметические операции.
|
||||
|
|
|
|||
Loading…
Reference in a new issue