Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2007.12.30;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.5 MB
Время: 0.009 c
2-1196969426
smartleds
2007-12-06 22:30
2007.12.30
Коллеги , когда при работе с StringGrid


15-1196343933
Anatoly Podgoretsky
2007-11-29 16:45
2007.12.30
Возможно интересно тем, кто хочет купить старые версии Дельфи


15-1196339700
Leonid_P
2007-11-29 15:35
2007.12.30
PHP: получить "заголовки" EXE файла.


2-1196782477
Alexey
2007-12-04 18:34
2007.12.30
Как правильно масштабировать форму


6-1176735518
Dmitry_177
2007-04-16 18:58
2007.12.30
Остановить выполнение accept или recv





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский