This repository has been archived on 2024-12-25. You can view files and clone it, but cannot push or open issues or pull requests.
OldPascalProjects/BlockFileOfT/FileDatabase/База данных на файле.pas
2024-03-10 20:32:51 +03:00

201 lines
5.2 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.