Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.12.30;
Скачать: CL | DM;

Вниз

Работа с делфяными строками на билт-ин асме   Найти похожие ветки 

 
Dib@zol ©   (2007-12-06 19:43) [0]

Здравствуйте ещё раз. В рамках продвинутого изучения делфей решил разобраться, как реализовать низкоуровневую работу со строками. На первый взгляд всё просто: string-переменная - это указатель на некую область памяти, размер которой задан по смещению -4 от указателя, а количество переменных, ссылающихся на этот указатель - по смещению -8. Попробовал было я создать такой массив байт в памяти вручную. Ан нет - не получается! Функция моя выдаёт пустую строку. Выходит, я не выполнил какое-то условие для правильного создания. Начал рыться в Гугле - ничего не нашёл. Было динамическое распределение памяти, но о строках как таковых было мало вразумительного. Пожалуйста, помогите теоретическим материалом (на тему управления памятью под строки в Делфях)!

Вот моя нерабочая функция:

function CreateString(Len:DWORD):string;
asm
 PUSH ECX;

 ADD EAX, 8;
 PUSH EAX;
 CALL GetMemory;
 MOV ECX, EAX;
 POP EAX;
 XCHG EAX, ECX;
 ADD EAX, 8;

 MOV DWORD PTR [EAX-8], 0;
 SUB ECX, 8;
 MOV DWORD PTR [EAX-4], ECX;

 DEC EAX;
 @loop:
   MOV BYTE PTR [EAX+ECX], "q";
 LOOP @loop;
 INC EAX;

 POP ECX;
end;


 
Юрий Зотов ©   (2007-12-06 20:21) [1]

В код не вникал, а в определении строки есть существенная неточность:

...это указатель на некую область памяти, которая заканчивается нулевым байтом, размер которой...


 
Юрий Зотов ©   (2007-12-06 21:26) [2]

> Dib@zol ©   (06.12.07 19:43)

Если уж есть такое неуемное желание (правда, непонятно, зачем), то переведите на Асм функцию NewStr или вот эту самоделку.

type
 PAnsiStringRecord = ^TAnsiStringRecord;
 TAnsiStringRecord = record
   RefCount: cardinal;
   BodySize: cardinal;
   Body: char;
 end;

function NewAnsiString(Source: string): string;
var
 AnsiStringRecord: PAnsiStringRecord;
begin
 GetMem(AnsiStringRecord, SizeOf(TAnsiStringRecord) + Length(Source));
 AnsiStringRecord^.RefCount := 0;
 AnsiStringRecord^.BodySize := Length(Source) + 1;
 if Source <> "" then
   CopyMemory(@AnsiStringRecord^.Body, @Source[1], AnsiStringRecord^.BodySize)
 else
   AnsiStringRecord^.Body := #0;
 Result := PChar(@AnsiStringRecord^.Body)
end;


 
Rouse_ ©   (2007-12-06 21:37) [3]

хм.
А где call System.@UniqueString? Где выставление размера? Где рефы? Почему стек бьется?
Нет. Не нужно пока тебе самому писать работу со строками - изучи работу стандартного менеджера памяти сначала...


 
oxffff ©   (2007-12-06 22:44) [4]


> Dib@zol ©   (06.12.07 19:43)  


RTFM

function _NewAnsiString(length: Longint): Pointer;
{$IFDEF PUREPASCAL}
var
 P: PStrRec;
begin
 Result := nil;
 if length <= 0 then Exit;
 // Alloc an extra null for strings with even length.  This has no actual cost
 // since the allocator will round up the request to an even size anyway.
 // All widestring allocations have even length, and need a double null terminator.
 GetMem(P, length + sizeof(StrRec) + 1 + ((length + 1) and 1));
 Result := Pointer(Integer(P) + sizeof(StrRec));
 P.length := length;
 P.refcnt := 1;
 PWideChar(Result)[length div 2] := #0;  // length guaranteed >= 2
end;
{$ELSE}
asm
 { ->    EAX     length                  }
 { <-    EAX pointer to new string       }

         TEST    EAX,EAX
         JLE     @@null
         PUSH    EAX
         ADD     EAX,rOff+2                       // one or two nulls (Ansi/Wide)
         AND     EAX, not 1                   // round up to even length
         PUSH    EAX
         CALL    _GetMem
         POP     EDX                              // actual allocated length (>= 2)
         MOV     word ptr [EAX+EDX-2],0           // double null terminator
         ADD     EAX,rOff
         POP     EDX                              // requested string length
         MOV     [EAX-skew].StrRec.length,EDX
         MOV     [EAX-skew].StrRec.refCnt,1
         RET
@@null:
         XOR     EAX,EAX
end;
{$ENDIF}


 
Dib@zol ©   (2007-12-07 09:09) [5]

> А где call System.@UniqueString?
В том-то и дело, что я хотел понять, как он работает.

> Где выставление размера?
MOV DWORD PTR [EAX-4], ECX;

> Где рефы?
MOV DWORD PTR [EAX-8], 0;

> Почему стек бьется?
А он не бьётся. Количество PUSH"ей абсолютно равно количеству POP"ов. Если бы стек бился, после выполнения немедленно возник бы AV.

А остальным - спасибо большое за справочную информацию.


 
Rouse_ ©   (2007-12-07 10:17) [6]


> Если бы стек бился, после выполнения немедленно возник бы AV.

Что собственно и наблюдается :)


 
Dib@zol ©   (2007-12-07 10:23) [7]

А у меня почему-то не наблюдается... На какой винде тестировали? Я на 98-ой, однако вчера это писалось на ХРеновине, и тоже не вызывало AV.


 
Rouse_ ©   (2007-12-07 10:50) [8]

Дома на ХР протестировал.


 
Сергей М. ©   (2007-12-07 10:50) [9]


> Dib@zol ©   (07.12.07 09:09) [5]


ну ты главную-то свою ошибку осознал ?


 
Dib@zol ©   (2007-12-07 10:55) [10]

> ну ты главную-то свою ошибку осознал ?

Что количество символов должно быть кратно 2, и завершаться должно как минимум одним нулём? Если это оно, то да.


 
Dib@zol ©   (2007-12-07 11:00) [11]

ЗЫ А кто всё-таки может поделиться ссылочкой на теорию по теме "механизмы работы типа string"?
ЗЗЫ Сергей М - анкета не найдена... :)


 
Сергей М. ©   (2007-12-07 11:01) [12]


> Dib@zol ©   (07.12.07 10:55) [10]


Это не главная ошибка.

Главная состоит в том, что ты игнорируешь неявный переданный тебе в edx параметр. Результат работы ф-ции ты обязан записать по адресу, который передан тебе в edx. А ты возвращаешь рез-т в eax, что тут же ведет к утечке памяти.


 
Dib@zol ©   (2007-12-07 11:12) [13]

> ты игнорируешь неявный переданный тебе в edx параметр

o_0 То есть ты хочешь сказать, что GetMemory что-то ещё пишет в EDX??? Но ведь она работает под директивой __fastcall, в которой про подобное ничего не указано! То есть использование GetMemory под чистый Паскаль, где к EDX обратиться нельзя, неизбежно приведёт к мем-лику??? И опять же, по правилам __fastcall"а результат должен передаваться через ЕАХ! Поясни пожалуйста!


 
Сергей М. ©   (2007-12-07 11:24) [14]


> ты хочешь сказать, что GetMemory что-то ещё пишет в EDX?


Нет, она возвращает результат в eax.


> Но ведь она работает под директивой __fastcall


Верно. И ее результат ты совершенно справедливо забираешь из eax.

Но я не о вызове GetMemory, а о вызове твоей функции. Именно ей компилятор передает в edx адрес, по которому в рез-те ты должен записать ссылку на аллокированную тобой строку.

Вся петрушка в том, что компилятор, видя типом твоей ф-ции тип LargeString, на самом деле рассматривает твою ф-цию как процедуру, передавая тебе в полном соответствии с pascal factcall в EAX требуемый размер будущей строки и в EDX адрес, по которому он ожидает увидеть структуру, определяющую аллокированную тобой строку.


 
Ins ©   (2007-12-07 11:41) [15]


> ЗЫ А кто всё-таки может поделиться ссылочкой на теорию по теме
>"механизмы работы типа string"?


http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1206
http://www.rsdn.ru/article/Delphi/dynarrays.xml


 
Dib@zol ©   (2007-12-07 11:47) [16]

> [14] Сергей М. ©   (07.12.07 11:24)

Во! Спасибо, что ткнул носом! Блин, а я и забыл совсем...

Короче вот окончательный вариант:

function CreateString(Len:DWORD):string;
asm
 TEST EAX, EAX;
 JLE @null;
 PUSH ECX;
 PUSH EDX;
 ADD EAX, 10;
 AND EAX, -2;
 PUSH EAX;
 CALL GetMemory;
 POP ECX;
 MOV WORD PTR [EAX+ECX-2], 0;
 ADD EAX, 8;
 MOV [EAX-4], ECX;
 MOV [EAX-8], 1;
 DEC EAX;
 SUB ECX, 10;
 @loop:
 MOV BYTE PTR [EAX+ECX], "q";
 LOOP @loop;
 INC EAX;
 POP EDX;
 MOV DWORD PTR [EDX], EAX;
 POP ECX;
 RET;
 @null:
 XOR EAX, EAX;
end;

> [15] Ins ©   (07.12.07 11:41)
Отдельное спасибо!


 
Сергей М. ©   (2007-12-07 11:52) [17]


> я и забыл совсем


А отладчик на что ?)



Страницы: 1 вся ветка

Текущий архив: 2007.12.30;
Скачать: CL | DM;

Наверх




Память: 0.52 MB
Время: 0.016 c
3-1187186976
greg123
2007-08-15 18:09
2007.12.30
Использование хранимых процедур в MS SQL SERVER 2000


3-1188310105
rar
2007-08-28 18:08
2007.12.30
Загрузить картинку в базу Oracle


2-1196763788
Alexandr Malygin
2007-12-04 13:23
2007.12.30
динамическое создание/удаление компонент


15-1196074479
No_Dead
2007-11-26 13:54
2007.12.30
Логика БД


15-1196419617
zlodiy
2007-11-30 13:46
2007.12.30
Восстановление данных