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.