Размышления о преобразованиях

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Размышления о преобразованиях

Сообщение Troublemaker » 07.05.2008 18:53:12

Я уже не раз упоминал Clarion - это СУБД такая с нехилым универсальным языком программирования. В нем слово record относится только к файлам или таблицам БД, а точный синоним паскалевской record в нем - group.
Приятная черта языка - декларация over. Например:
Код: Выделить всё
Bytes   byte,dim(4)  !массив из четырех байтов
ALong  long,over(bytes)

Метки всегда начинаются с первого символа строки, операторы - со второго или последующих. Метка от описания отделяется пробелом или табуляцией. Точка с запятой в качестве разделителя операторов поддерживается, но обязательна только при записи нескольких операторов в одной строке, а так концом оператора является конец строки. Восклицательный знак - начало строчного комментария. Операторные скобки не используются, в конце составного оператора может стоять или слово end или точка. Секция описания данных начинается с начала исходника и заканчивается на ключевом слове code, которое не нуждается в закрывающем "тэге" типа "end."
Вышеприведенная запись обозначает, что переменные Bytes и ALong занимают одно и то же место в памяти. В Pascal такое возможно, как я знаю, только для вариантных частей record-ов и невозможно для обычных переменных.

Так вот, у этой group (и родственной ей файловой record) есть одна примечательная черта - группа рассматривается как строка длиной sizeof(компоненты_группы), и я могу обратиться к любому байту группы как к элементу массива:
Код: Выделить всё
AGroup group
Bytes byte,dim(10)
Str   string(20) !строка длиной 20 символов
  .
SuperStr string(size(AGroup)),over(AGroup) !просто строка поверх группы

  code !начало исполняемой секции исходника
  agroup.str='freepascal.ru'
  agroup.bytes[1]=5
  agroup[11]='g' !11-ый байт группы, он же - первый байт переменной agroup.str, меняется на 'g' и получаем 'greepascal.ru'
  SuperStr[11]='f' !и снова возвращаемся к 'freepascal.ru'
Это я всё вот к чему: если у меня в паскале описан какой-то тип и есть переменная этого типа (посложнее чем array of char), то единственный способ получить доступ к содержимому этой переменной - это создать супертип вроде:
Код: Выделить всё
type rec=record
bla-bla-bla
end;
superrec=record
case boolean of
true:  (bla:rec);
false: (blabla:array[sizeof(rec)] of char
end;

По приведенным выше причинам я считаю такой подход правильным, но не слишком удобным - на фига два типа (или один, но сразу - запись с вариантами). А что скажут более опытные паскалеведы? Может есть что-то, о чем я не знаю?

Где-то в TFM я видел нечто, похожее на varptr в бейсике, то есть - способ получить адрес переменной в памяти, но что я там найду? Чистые данные или данные и служебный оверхэд?

Для чего это нужно? Да элементарно! Всё та же система тестирования - для удобства работы мне нужны сложные структуры, а для удобства шифрования мне проще с ними работать как с потоком байтов или символов, чтобы в файл писать просто массив данных безо всякой видимой глазом структуры.
Последний раз редактировалось Troublemaker 08.05.2008 11:49:03, всего редактировалось 1 раз.
Аватара пользователя
Troublemaker
постоялец
 
Сообщения: 292
Зарегистрирован: 16.04.2008 13:00:44
Откуда: Биробиджан, Дальний Восток

Re: Размышления о преобразованиях

Сообщение Sergei I. Gorelkin » 07.05.2008 22:16:10

Паскаль - весьма низкоуровневый язык.
Во-первых, в нем есть ключевое слово absolute, позволяющее определять переменные "одну поверх другой".
Во-вторых, есть указатели, с помощью которых можно вообще что угодно творить (в том числе и прострелить себе ногу).
Служебного оверхэда почти что нет, но, например, в строке типа shortstring в нулевом байте лежит длина строки, а строки типа ansistring или widestring, динамические массивы и классы сами по себе являются указателями.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Размышления о преобразованиях

Сообщение Vadim » 08.05.2008 07:30:00

Troublemaker писал(а):для удобства работы мне нужны сложные структуры, а для удобства шифрования мне проще с ними работать как с потоком байтов или символов, чтобы в файл писать просто массив данных безо всякой видимой глазом структуры.

Может я не совсем понял суть претензии к Паскалю :) , но ведь можно любые типы данных записать в поток (TStream, TFileStream) и уже с этим потоком работать как с последовательностью байтов - шифруйте, дешифруйте, удаляйте, добавляйте байты, в общем, что хотите то и делайте.
Зашифруем, для примера, файлик типа .dbf:
Код: Выделить всё
Const
password = 8;
Var
f: TfileStream;
i: int64;
b: byte;
Begin
f:=TFileStream.Create('myfile.dbf', fmOpenReadWrite);
For i=0 To f.Size-1 Do
Begin
f.Read(b, SizeOf(b));
b:=b XOR password;
f.Seek(-1, soFromCurrent);
f.Write(b, SizeOf(b));
End;
End;

У компонента TDBF есть интересное свойство - UserStream: TStream, который содержит как раз этот самый прочитанный поток из файла. Так что "незаметное" для пользователя шифрование-дешифрование становится и совсем уже пустяковым делом. :) На зашифрованый таким образом файл можно пялиться сколько угодно - там будет сплошная абракадабра. Его нельзя открыть стандартными средствами просмотра DBF файлов - DataBase Desktop, Visual FoxPro и т.п., потому как зашифрован и заголовок файла тоже.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Размышления о преобразованиях

Сообщение Troublemaker » 08.05.2008 09:59:07

Sergei I. Gorelkin писал(а):есть ключевое слово absolute
О! Про него я просто забыл, хотя когда-то даже использовал. Спасибо за напоминание.
Vadim писал(а):Может я не совсем понял суть претензии к Паскалю
А претензии никакой нет. Я описал механизм, существующий в другом языке, и поинтересовался, есть ли его аналог в Паскале.
Мне приходилось работать в техподдержке, и я прекрасно знаю, как раздражают заявления пользователей в духе "А вот ваша фигня того-то не умеет!". Начинаешь копать и выясняется, что пользователь попросту счел ниже своего достоинства заглянуть в TFM, где "то-то" вынесено на первую страницу 64-ым шрифтом.
Увы, состояние FM для FPC оставляет желать лучшего. Поэтому я задаю свои вопросы не в духе "не умеет, тупое животное", а в духе "может и умеет, но я не знаю, как"
За идею насчет стримов - тоже спасибо, хотя в приведенном примере я не понял, в чем преимущество перед обычными файловыми операциями, если тот же dbf открыть через reset(file,1)

Тем не менее, речь шла опять же о структурах в памяти. Если я буду через absolute описывать байтовый массив поверх record-a, то есть, как я понимаю, шанс повредить тот самый оверхэд?
Аватара пользователя
Troublemaker
постоялец
 
Сообщения: 292
Зарегистрирован: 16.04.2008 13:00:44
Откуда: Биробиджан, Дальний Восток

Re: Размышления о преобразованиях

Сообщение Vadim » 08.05.2008 10:30:55

Troublemaker писал(а):в чем преимущество перед обычными файловыми операциями, если тот же dbf открыть через reset(file,1)

Преимущество в совместимости типа данных c TDBF. Если интересуют какие-то другие преимущества, то надо сказать конкретно в какой области.
Troublemaker писал(а):...absolute...

Ты сначала проверь, так сказать "вообще", будет ли такая конструкция работать применительно к твоей конкретной задаче. Не исключено, что компилятор вообще не даст использовать это слово в программе.
Для примера: в досовском Паскале всевозможные и многочисленные любители убыстрять работу за счёт непосредственного залезания руками в оперативную память любили использовать для доступа к видеопамяти вот такую конструкцию:
Код: Выделить всё
Var
videomemory: array[0..2000] of byte absolute $B800:$0000; //Пишу просто по памяти, конкретные цифры размера массива и адреса видеопамяти значения не имеют :)

В Turbo Pascal это можно было использовать без проблем, в Borland Pascal только в реальном режиме, в защищённом вываливается исключение. Аналогичная ситуация и с FreePascal.
Так что сначала проверь, можно ли вообще будет использовать такую конструкцию.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Размышления о преобразованиях

Сообщение v-t-l » 08.05.2008 10:58:22

Troublemaker писал(а):За идею насчет стримов - тоже спасибо, хотя в приведенном примере я не понял, в чем преимущество перед обычными файловыми операциями, если тот же dbf открыть через reset(file,1)

Код: Выделить всё
procedure TForm1.FormCreate(Sender: TObject);
var
  ms: TMemoryStream;
begin
  ms := TMemoryStream.Create;
  ms.LoadFromFile('tbl1.dbf');
  {
  здесь дешифруем
  ...
  }
  Dbf1.UserStream := ms; // может быть, нужно поменять местами со следующей строкой
  Dbf1.Storage:=stoMemory;
 
  Dbf1.Active := True;
end;
v-t-l
энтузиаст
 
Сообщения: 740
Зарегистрирован: 13.05.2007 16:27:22
Откуда: Belarus

Re: Размышления о преобразованиях

Сообщение Иван Шихалев » 08.05.2008 11:33:30

Vadim писал(а):Так что сначала проверь, можно ли вообще будет использовать такую конструкцию.

Естественно, с конкретным адресом FPC работать не даст, а вот:
Код: Выделить всё
var X : byte;
var Y : char absolute X;

Отработает без всяких проблем, а именно это — аналог 'over' из начального поста.
Аватара пользователя
Иван Шихалев
энтузиаст
 
Сообщения: 1138
Зарегистрирован: 15.05.2006 11:26:13
Откуда: Екатеринбург

Re: Размышления о преобразованиях

Сообщение Troublemaker » 08.05.2008 11:47:45

Кстати, а вот здесь:
Код: Выделить всё
f.Read(b, SizeOf(b));
b:=b XOR password;
f.Seek(-1, soFromCurrent);
не должно ли смещение в f.seek так же быть равно -sizeof(b) на случай, если b будет не байтом?
Аватара пользователя
Troublemaker
постоялец
 
Сообщения: 292
Зарегистрирован: 16.04.2008 13:00:44
Откуда: Биробиджан, Дальний Восток

Re: Размышления о преобразованиях

Сообщение Vadim » 08.05.2008 12:34:00

Troublemaker писал(а):не должно ли смещение в f.seek так же быть равно -sizeof(b) на случай, если b будет не байтом?

Это просто пример. Естественно, в реальной программе нужно указать именно столько байт, на сколько нужно проводить смещение. Только хочу обратить внимание, что в приведённом мною примере, шифрование идёт именно байта. XOR - способ довольно простой и нересурсозатратный, однако от дурака и лоботряса, который хочет знать все ответы без прикладывания умственных усилий, защищает надёжно.
Иван Шихалев писал(а):Отработает без всяких проблем, а именно это — аналог 'over' из начального поста.

Таки да, отработает. :) Однако из конкретики того же первого поста, да и обсуждения проблем товарища Troublemaker:) , речь шла о шифровании файла и в этом случае проще, а главное понятнее в исходниках, будет отработать шифрование потока, чем наворачивать применение absolute, понятность которого, в приведённом тобою примере, отнюдь не очевидна. И поверь, точно так же это будет непонятно выглядеть и в реальной программе. :)
Последний раз редактировалось Vadim 08.05.2008 12:54:17, всего редактировалось 1 раз.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Размышления о преобразованиях

Сообщение Sergei I. Gorelkin » 08.05.2008 12:51:13

Troublemaker писал(а):Тем не менее, речь шла опять же о структурах в памяти. Если я буду через absolute описывать байтовый массив поверх record-a, то есть, как я понимаю, шанс повредить тот самый оверхэд?

В записях нет служебной информации, но могут быть байты выравнивания. Например, если первый элемент - байт, а запись объявлена с {$PACKRECORDS 4}, то между первым и вторым элементом будет 3 выравнивающих байта. Повредить эти байты невозможно - программа при доступе к записи просто их не видит, и содержимое ей безразлично (хотя, это мое утверждение не мешало бы проверить на практике). Если речь идет о шифровании, писать туда всякий мусор может быть даже полезно.

Если указана директива {$PACKRECORDS 1}, а запись объявлена как packed - то выравнивания не будет.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Размышления о преобразованиях

Сообщение Troublemaker » 08.05.2008 13:07:39

Vadim писал(а):речь шла о шифровании файла
Применение absolute - вполне очевидно. А вот насчет шифрования файла не всё так гладко, как хотелось бы.

Не нашел способа ВСТАВИТЬ что-то в поток, в тот же memstream. Например, вписать в начало файла ключ шифрования. (или другие данные, не принципиально). Или создавать один поток для ввода, другой для вывода?
Sergei I. Gorelkin писал(а):Повредить эти байты невозможно - программа при доступе к записи просто их не видит, и содержимое ей безразлично
Это если доступ на уровне полей записи. Но я-то лезу просто к участку памяти, занятой ей...
Хотя, действительно, объявлю-ка я все записи не как упакованные, и тогда порча выравнивающих заполнителей будет мне по барабану. Или лучше принудительно выставить {$PACKRECORDS 1}, чтобы записи занимали минимум места?
Аватара пользователя
Troublemaker
постоялец
 
Сообщения: 292
Зарегистрирован: 16.04.2008 13:00:44
Откуда: Биробиджан, Дальний Восток

Re: Размышления о преобразованиях

Сообщение shade » 08.05.2008 13:48:43

Troublemaker писал(а):Для чего это нужно? Да элементарно! Всё та же система тестирования - для удобства работы мне нужны сложные структуры, а для удобства шифрования мне проще с ними работать как с потоком байтов или символов, чтобы в файл писать просто массив данных безо всякой видимой глазом структуры.

В корне не верный подход. Чтобы решить эту задачу не надо менять синтаксис языка. Я ж писал, что луший способ это написать класс, потомок от TStream. Например
Код: Выделить всё
program testcrypt;

{$ifdef fpc}
{$mode objfpc}
{$endif}

uses SysUtils, Classes;

type
  TCryptStream = class (TStream)
    protected
      FStream: TStream;
    public
      constructor Create(AStream: TStream);
      function Read(var buf; len: Longint): Longint; override;
      function Write(const buf; len: Longint): Longint; override;
  end;

constructor TCryptStream.Create(AStream: TStream);
begin
  inherited Create;
  FStream := AStream;
end;

function TCryptStream.Read(var buf; len: Longint): Longint;
var
  p: pbyte;
  i: Longint;
begin
  Result := FStream.Read(buf, len);
  p := pbyte(@buf);
  for i := 0 to Result-1 do
  begin
    p[i] := p[i] - 3;
  end; // for i
end;

function TCryptStream.Write(const buf; len: Longint): Longint;
var
  p, cbuf: pbyte;
  i: Longint;
begin
  GetMem(cbuf, len);
  p := pbyte(@buf);
  try
    for i := 0 to len-1 do
    begin
      cbuf[i] := p[i] + 3;
    end; // for i
    Result := FStream.Write(cbuf^, len);
  finally
    FreeMem(cbuf);
  end;
end;

procedure crypt(const FileName, Message: string);
var
  fs: TFileStream;
  cs: TCryptStream;
  len: Integer;
begin
  fs := TFileStream.Create(FileName, fmCreate);
  try
    cs := TCryptStream.Create(fs);
    try
      len := length(Message);
      cs.WriteBuffer(len, sizeof(len));
      cs.WriteBuffer(Message[1], len);
    finally
      cs.Free;
    end;
  finally
    fs.Free;
  end;
end;

function decrypt(const FileName: string): string;
var
  fs: TFileStream;
  cs: TCryptStream;
  len: Integer;
begin
  fs := TFileStream.Create(FileName, fmOpenRead);
  try
    cs := TCryptStream.Create(fs);
    try
      cs.ReadBuffer(len, sizeof(len));
      SetLength(Result, len);
      cs.ReadBuffer(Result[1], len);
    finally
      cs.Free;
    end;
  finally
    fs.Free;
  end;
end;

const
  FileName = 'test.bin';

var
  message: string;
begin
  if (ParamCount = 0) or (ParamStr(1) = 'crypt') then
  begin
    write('input message for crypt: ');
    readln(message);
    crypt(FileName, message);
    Exit;
  end; // if
  if ParamStr(1) = 'decrypt' then
  begin
    message := decrypt(FileName);
    writeln('decrypted message: ', message);
  end; // if
end.

Тестируем:
Код: Выделить всё
alex@home:~/doc> ./testcrypt
input message for crypt: Hello world
alex@home:~/doc> ./testcrypt decrypt
decrypted message: Hello world


В реальности код будет чуть сложнее (наверняка потребуется буферизация, метод Flush, возможно прийдется разбить на два класса шифрующий/дешифрующий и т.п.), но как видите никаких заморочек в вариантнымим записями не нужны.
Аватара пользователя
shade
энтузиаст
 
Сообщения: 879
Зарегистрирован: 21.02.2006 20:15:48
Откуда: http://shamangrad.net/


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2

Рейтинг@Mail.ru