Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2 3 4   вперед  Ctrl      все
 Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
Здравствуйте эксперты по Delphi!.
Мне потребовалось написать такой XML генератор, который работает очень быстро.
Но не знаю где допускал ошибку.

Версия Delphi 6. (Я вынужден написать на Delphi 6. Другого выхода нет.)

Какие ошибки:
- под ОС Win 7 : AccessViolation;
- под ОС Win XP : медленно работает;

Я думаю все с PChar.

Уважаемые эксперты, посмотрите на мой код:
+ TXMLWriter

  unit XMLWriter;

interface

uses
  Classes, SysUtils;

type

  TXmlStandalone = (xsOmit, xsYes, xsNo);

  TXmlCloseTag = (xtNone, xtClose, xtSlashClose);

  TXMLWriter = class
  private
    FCapacity: LongInt;
    FLength: Integer;
    FBuffer: PChar;
    procedure Grow;
  public
    constructor Create(const Standalone: TXmlStandalone);
    destructor Destroy; override;
    procedure WriteValue(const Buffer: PChar);
    procedure OpenElement(const Name: PChar; const CloseTag: TXmlCloseTag);
    procedure WriteElement(const Name, Value: PChar);
    procedure WriteAttribute(const Name, Value: PChar; const CloseTag: TXmlCloseTag); overload;
    procedure WriteBuffer(const Buffer: PChar);
    procedure WriteChar(const Value: Char);
    procedure CloseElement(const Name: PChar);
  end; 

implementation

const

  cOpenTag       = '<';
  cCloseTag      = '>';
  cSlash         = '/';
  cOpenSlashTag  = '</';
  cSlashCloseTag = '/>';
  cSpace         = ' ';
  cEquality      = '=';
  cApostrophe    = '"';
  cEqualApos     = '="';
  cXml           = '<?xml version="1.0" encoding="UTF-8" standalone="%s" ?>';
  cStandalones   : array [TXmlStandalone] of string = ('omit', 'yes', 'no');

{ TXMLWriter }

constructor TXMLWriter.Create(const Standalone: TXmlStandalone);
begin
  FCapacity := 0;
  FLength := 0;

  Grow;
  // xml declarations
  WriteBuffer(PChar(Format(cXml, [cStandalones[Standalone]])));
end;

destructor TXMLWriter.Destroy;
begin
  if FCapacity > 0 then
    FreeMem(FBuffer);
  FBuffer := nil;
  inherited Destroy;
end;

procedure TXMLWriter.Grow;
var
  NewCapacity: Integer;
begin
  NewCapacity := FCapacity + $200;
  if FCapacity = 0 then
  begin
    GetMem(FBuffer, NewCapacity * SizeOf(Char))
  end
  else
  begin
    ReallocMem(FBuffer, NewCapacity * SizeOf(Char));
  end;
  FCapacity := NewCapacity;
end;

procedure TXMLWriter.OpenElement(const Name: PChar; const CloseTag: TXmlCloseTag);
begin
  // <
  WriteChar(cOpenTag);
  // name
  WriteBuffer(Name);
  // >
  case CloseTag of
    xtClose: WriteChar(cCloseTag);
    xtSlashClose: WriteBuffer(cSlashCloseTag);
  end;
end;

procedure TXMLWriter.CloseElement(const Name: PChar);
begin
  // </
  WriteBuffer(cOpenSlashTag);
  // name
  WriteBuffer(Name);
  // >
  WriteChar(cCloseTag);
end;


procedure TXMLWriter.WriteElement(const Name, Value: PChar);
begin
  OpenElement(Name, xtClose);
  if Value <> nil then
  begin
    // value
    WriteValue(Value);
    // </name>
    CloseElement(Name);
  end
  else
    // />
    WriteBuffer(cSlashCloseTag);
end;


procedure TXMLWriter.WriteAttribute(const Name, Value: PChar; const CloseTag: TXmlCloseTag);
begin
  // space
  WriteChar(cSpace);
  // name
  WriteBuffer(Name);
  // ="
  WriteBuffer(cEqualApos);
  // value
  WriteValue(Value);
  // "
  WriteChar(cApostrophe);
  // >
  case CloseTag of
    xtClose: WriteChar(cCloseTag);
    xtSlashClose: WriteBuffer(cSlashCloseTag);
  end;
end;


procedure TXMLWriter.WriteBuffer(const Buffer: PChar);
var
  P: PChar;
begin
  P := Buffer;  
  while P^ <> #0 do begin
    (FBuffer + FLength)^ := P^;
    Inc(FLength);
    if FLength >= FCapacity then
      Grow;
    Inc(P);
  end;
end;

procedure TXMLWriter.WriteChar(const Value: Char);
begin
  (FBuffer + FLength)^ := Value;
  Inc(FLength);
  if FLength >= FCapacity then
    Grow;
end;


procedure TXMLWriter.WriteValue(const Buffer: PChar);
const
   clt  = '%lt;';
   cgt  = '%gt;';
   cmp  = '&amp;';
   cqt  = '&quot;';
var
  P: PChar;
  n : Integer;
begin
  P := Buffer;
  while P^ <> #0 do
  begin
    case P^ of
      '<':
        begin
          WriteBuffer(clt);
          Inc(P);
        end;
      '>':
        begin
          WriteBuffer(cgt);
          Inc(P);
        end;
      '&':
        begin
          WriteBuffer(cmp);
          Inc(P);
        end;
      '"':
        begin
          WriteBuffer(cqt);
          Inc(P);
        end
    else
      begin
         (FBuffer + FLength)^ := P^;
         Inc(FLength);
         if FLength = FCapacity then
           Grow;
         Inc(P);
      end;
    end
  end;
end;

end.



Заранее спасибо!
19 июн 17, 08:28    [20573645]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
Вот тест:
    procedure TForm1.btn1Click(Sender: TObject);
const
   c1:PChar = 'root';
   c2:PChar = 'chs';
   c3:PChar = 'i';
   c4:PChar = 'chd';
   c5:PChar = 'n';

var
  xWriter : TXMLWriter;
  i, j : Integer;
  p : PChar;
begin
  xWriter := TXMLWriter.Create(xsYes);
  try
    xWriter.OpenElement(c1, xtClose);
    for i := 0 to 10000 do
    begin
      xWriter.OpenElement(c2, xtNone);
      p := PChar(IntToStr(i));
      xWriter.WriteAttribute(c3, p, xtClose);

      for j := 0 to 50 do
      begin
        xWriter.OpenElement(c4, xtNone);
        p := PChar(IntToStr(j));
        xWriter.WriteAttribute(c5, p, xtClose);
        xWriter.CloseElement(c4);
      end;
      xWriter.CloseElement(c2);
    end;
    xWriter.CloseElement(c1);

    ShowMessage('ok');
  finally
    xWriter.Free;
  end;
end;
  
19 июн 17, 08:33    [20573650]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
Очень нужна ваша помощь!
19 июн 17, 09:02    [20573681]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Maxim Rusov
Member

Откуда: Москва-Питер
Сообщений: 2387
Измените Grow типа:

procedure TXMLWriter.Grow;
var
  NewCapacity: Integer;
begin
  NewCapacity := FCapacity + FCapacity div 4;
19 июн 17, 10:13    [20573807]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Softologic
Member

Откуда: Москва
Сообщений: 73
Alimkulov
Очень нужна ваша помощь!

Лучше возьми NativeXml и не изобретай велосипед. Бесплатно, доступно для Д6 и легко в освоении.
19 июн 17, 10:14    [20573811]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
alekcvp
Member

Откуда:
Сообщений: 45
1. А в дебаггере на какой именно строке вылетает Access Violation? Потому что у меня на XE2 и Windows 7 не воспроизводится (скопировал код из btn1Click в консольное приложение).
2. Не по теме, но если вам важна скорость, то ИМХО вместо FBuffer и FLength сделать FBuffer, FCursor, FLast: PChar, примерно так:
while P^ <> #0 do begin
  FCursor^ := P^;
  Inc(FCursor);
  if FCursor >= FLast then 
    Grow;
  Inc(P^);

3. Есть ли объективная причина везде использовать PChar?.. Если не планируется выносить код в DLL, то может всё-таки string?
А в коде, соответственно:
procedure TXMLWriter.WriteValue(const Buffer: string);
...
var
  P: PChar;
...
begin
  if Buffer <> '' then begin
    P := Pointer(Buffer);
    while P^ <> #0 do
19 июн 17, 10:17    [20573820]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
alekcvp
вместо FBuffer и FLength сделать FBuffer, FCursor, FLast: PChar

Спасибо за предложения. Как решу проблему, обязательна так и делаю.
19 июн 17, 10:33    [20573862]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
schi
Member

Откуда: Москва
Сообщений: 1591
Alimkulov
Здравствуйте эксперты по Delphi!.
Мне потребовалось написать такой XML генератор, который работает очень быстро.


быстрее writeln вряд ли что-то создает xml
19 июн 17, 10:33    [20573863]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
Softologic,
Спасибо за совет! NativeXml соответствует ли требованиям (сверхлимитный скорость)?
19 июн 17, 10:36    [20573872]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
alekcvp
1. А в дебаггере на какой именно строке вылетает Access Violation?


Не могу ловить строку AccessViolation. То OpenElement, в другой раз на WriteAttribute и.т.д.
Логировал в Grow и как NewCapacity получает значение свыше 2000000, везде AccessViolation.
19 июн 17, 10:45    [20573892]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
alekcvp
3. Есть ли объективная причина везде использовать PChar?.. Если не планируется выносить код в DLL, то может всё-таки string?
[/src]


Да конечно. Можно и так. Переделаю как вы указали.
19 июн 17, 10:48    [20573900]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
defecator
Member

Откуда: arm-pascal.ru
Сообщений: 32522
Alimkulov
Softologic,
Спасибо за совет! NativeXml соответствует ли требованиям (сверхлимитный скорость)?

а лимит какой?
19 июн 17, 11:39    [20574036]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
alekcvp
Member

Откуда:
Сообщений: 45
Alimkulov
alekcvp
1. А в дебаггере на какой именно строке вылетает Access Violation?

Не могу ловить строку AccessViolation. То OpenElement, в другой раз на WriteAttribute и.т.д.
Логировал в Grow и как NewCapacity получает значение свыше 2000000, везде AccessViolation.

А если сразу выделить, скажем, 4194304 (4 Mb), и увеличивать не по $200, а по $400000 - будут AV?
Еще вариант, подключить к тестовому проекту FastMM4 - с ним тоже AV?
19 июн 17, 11:41    [20574044]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
defecator
Member

Откуда: arm-pascal.ru
Сообщений: 32522
мне кажется, что не стоит бесконечно наращивать буфер, у тебя сильно дефрагментируется память
19 июн 17, 11:44    [20574052]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
defecator,
Я думал NativeXml формирует DOM, а на это уходить много времени.
А у меня задача: генерировать XML который объем свыше 50 mb.
19 июн 17, 11:49    [20574058]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Alimkulov
Member

Откуда:
Сообщений: 721
defecator
мне кажется, что не стоит бесконечно наращивать буфер, у тебя сильно дефрагментируется память

Какой вариант предлагаете?
19 июн 17, 11:51    [20574067]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
defecator
Member

Откуда: arm-pascal.ru
Сообщений: 32522
Alimkulov
defecator,
Я думал NativeXml формирует DOM, а на это уходить много времени.
А у меня задача: генерировать XML который объем свыше 50 mb.

возьми его и не парься
19 июн 17, 11:56    [20574084]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Aleksandr Sharahov
Member

Откуда: Москва
Сообщений: 1089
Alimkulov
defecator
мне кажется, что не стоит бесконечно наращивать буфер, у тебя сильно дефрагментируется память

Какой вариант предлагаете?


Зависит от того, что потом планируется делать с данными в буфере.

Можно писать буфер на диск и использовать старый буфер повторно для новой порции данных,
можно выделять новый буфер и писать новую порцию в него.
19 июн 17, 12:01    [20574107]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
Maxim Rusov
Member

Откуда: Москва-Питер
Сообщений: 2387
Grow измени, бладж!
19 июн 17, 12:16    [20574150]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
SOFT FOR YOU
Member

Откуда:
Сообщений: 2057
Alimkulov,

Я не знаю, почему у тебя AV, но я знаю, что ты достаточно долго мучаешься с памятью: реаллоки, проверки. Тебе нужно умно писать на диск, т.е. через буфер.

Тебе поможет библиотека CachedBuffers, там есть отдельный класс для файлов и есть высокоуровневой метод Write. Ты сможешь записывать как отдельно символы, так и строки.

Если в перспективе ты захочешь писать не только в UTF-8, но и в других кодировках, тогда тебе понадобится CachedTexts - там фишка в том, данные пишешь в одной кодировке, а на диск они кладутся в нужной тебе.
19 июн 17, 12:47    [20574280]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
defecator
Member

Откуда: arm-pascal.ru
Сообщений: 32522
SOFT FOR YOU
Alimkulov,

Я не знаю, почему у тебя AV, но я знаю, что ты достаточно долго мучаешься с памятью: реаллоки, проверки. Тебе нужно умно писать на диск, т.е. через буфер.

Тебе поможет библиотека CachedBuffers, там есть отдельный класс для файлов и есть высокоуровневой метод Write. Ты сможешь записывать как отдельно символы, так и строки.

Если в перспективе ты захочешь писать не только в UTF-8, но и в других кодировках, тогда тебе понадобится CachedTexts - там фишка в том, данные пишешь в одной кодировке, а на диск они кладутся в нужной тебе.


ему не надо тащить очередного монстра в проект, достаточно NativeXML
19 июн 17, 12:53    [20574311]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
SOFT FOR YOU
Member

Откуда:
Сообщений: 2057
defecator,

CachedBuffers меньше NatuveXml-я. Значительно. И дополнительных библиотек типа Classes не юзает. Совместим с KOL
19 июн 17, 12:56    [20574330]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
defecator
Member

Откуда: arm-pascal.ru
Сообщений: 32522
SOFT FOR YOU
defecator,

CachedBuffers меньше NatuveXml-я. Значительно. И дополнительных библиотек типа Classes не юзает. Совместим с KOL
зато NativeXML выполняет то, что нужно ТС.
19 июн 17, 13:10    [20574410]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
SOFT FOR YOU
Member

Откуда:
Сообщений: 2057
defecator,

Нет, он пишет в память. И выполняет тонну проверок и преобразований, которые в случае ТС скорее всего не нужны
19 июн 17, 13:17    [20574446]     Ответить | Цитировать Сообщить модератору
 Re: Работа с PChar  [new]
SOFT FOR YOU
Member

Откуда:
Сообщений: 2057
Автору нужна запись 50Мб быстро. CachedBuffers писал 100Мб на тестах за 100мск. О чем мы говорим?
19 июн 17, 13:25    [20574469]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2 3 4   вперед  Ctrl      все
Все форумы / Delphi Ответить