title: Can I Eliminate Extra Unicode String Calls (Delphi)
body: <p>I'm using Delphi 2009. In my program, I have been working very hard to optimize all my Delphi code for speed and memory use, especially my Unicode string handling.</p>
  
  <p>I have the following statement:</p>
  
  <pre><code>    Result := Result + GetFirstLastName(IndiID, 1);
  </code></pre>
  
  <p>When I debug that line, upon return from the GetFirstLastName function, it traces into the routine _UStrArrayClr in the System unit:</p>
  
  <pre><code>procedure _UStrArrayClr(var StrArray; Count: Integer);
  asm
          JMP     _LStrArrayClr
  end;
  </code></pre>
  
  <p>This calls _LStrArrayClr:</p>
  
  <pre><code>procedure       _LStrArrayClr(var StrArray; cnt: longint);
  {$IFDEF PUREPASCAL}
  var
    P: Pointer;
  begin
    P := @StrArray;
    while cnt &gt; 0 do
    begin
      _LStrClr(P^);
      Dec(cnt);
      Inc(Integer(P), sizeof(Pointer));
    end;
  end;
  {$ELSE}
  asm
          { -&gt;    EAX pointer to str      }
          {       EDX cnt         }
  
          PUSH    EBX
          PUSH    ESI
          MOV     EBX,EAX
          MOV     ESI,EDX
  
  @@loop:
          MOV     EDX,[EBX]                       { fetch str                     }
          TEST    EDX,EDX                         { if nil, nothing to do         }
          JE      @@doneEntry
          MOV     dword ptr [EBX],0               { clear str                     }
          MOV     ECX,[EDX-skew].StrRec.refCnt    { fetch refCnt                  }
          DEC     ECX                             { if &lt; 0: literal str           }
          JL      @@doneEntry
     LOCK DEC     [EDX-skew].StrRec.refCnt        { threadsafe dec refCount       }
          JNE     @@doneEntry
          LEA     EAX,[EDX-skew].StrRec.codePage  { if refCnt now zero, deallocate}
          CALL    _FreeMem
  @@doneEntry:
          ADD     EBX,4
          DEC     ESI
          JNE     @@loop
  
          POP     ESI
          POP     EBX
  end;
  {$ENDIF}
  </code></pre>
  
  <p>and runs through the loop once for each character, and on exit from there it calls _UStrCat:</p>
  
  <pre><code>procedure _UStrCat(var Dest: UnicodeString; const Source: UnicodeString);
  asm
          { -&gt;    EAX     pointer to dest }
          {       EDX source              }
  
          TEST    EDX,EDX       // Source empty, nop.
          JE      @@exit
  
          MOV     ECX,[EAX]     // ECX := Dest
          TEST    ECX,ECX       // Nil source =&gt; assignment
          JE      _UStrAsg
  
          PUSH    EBX
          PUSH    ESI
          PUSH    EDI
          MOV     EBX,EAX         // EBX := @Dest
          MOV     ESI,EDX         // ESI := Source
          CMP     ESI,ECX
          JE      @@appendSelf
  
          CMP     [ECX-skew].StrRec.elemSize,2
          JE      @@destIsUnicode
          CALL    _EnsureUnicodeString
          MOV     EDI,EAX
          MOV     ECX,EAX
  
  @@destIsUnicode:
          PUSH    0
          CMP     [ESI-skew].StrRec.elemSize,2
          JE      @@sourceIsUnicode
  
          MOV     EDI,ECX
          MOV     EAX,ESI
          MOV     [ESP],ESI
          CALL    _UStrAddRef
          MOV     EAX,ESP
          CALL    _EnsureUnicodeString
          MOV     ESI,[ESP]
          MOV     ECX,EDI
  
  @@sourceIsUnicode:
          MOV     EDI,[ECX-skew].StrRec.length  // EDI := Length(Dest)
          MOV     EDX,[ESI-skew].StrRec.length  // EDX := Length(Source)
          ADD     EDX,EDI         // EDX := (Length(Source) + Length(Dest)) * 2
          TEST    EDX,$C0000000
          JNZ     @@lengthOverflow
  
          MOV     EAX,EBX
          CALL    _UStrSetLength  // Set length of Dest
          MOV     EAX,ESI         // EAX := Source
          MOV     ECX,[ESI-skew].StrRec.length // ECX := Length(Source)
  
  @@noTemp:
          MOV     EDX,[EBX]       // EDX := Dest
          SHL     EDI,1           // EDI to bytes (Length(Dest) * 2)
          ADD     EDX,EDI         // Offset EDX for destination of move
          SHL     ECX,1           // convert Length(Source) to bytes
          CALL    Move            // Move(Source, Dest + Length(Dest)*2, Length(Source)*2)
          MOV     EAX,ESP         // Need to clear out the temp we may have created above
          MOV     EDX,[EAX]
          TEST    EDX,EDX
          JE      @@tempEmpty
  
          CALL    _LStrClr
  
  @@tempEmpty:
          POP     EAX
          POP     EDI
          POP     ESI
          POP     EBX
          RET
  
  @@appendSelf:
          CMP     [ECX-skew].StrRec.elemSize,2
          JE      @@selfIsUnicode
          MOV     EAX,EBX
          XOR     EDX,EDX
          CALL    _EnsureUnicodeString
          MOV     ECX,EAX
          MOV     EAX,EBX
  
  @@selfIsUnicode:
          MOV     EDI,[ECX-skew].StrRec.length
          MOV     EDX,EDI
          SHL     EDX,1
          TEST    EDX,$C0000000
          JNZ     @@lengthOverflow
          CALL    _UStrSetLength
          MOV     EAX,[EBX]
          MOV     ECX,EDI
          PUSH    0
          JMP     @@noTemp
  
  @@lengthOverflow:
          JMP     _IntOver
  
  @@exit:
  end;
  </code></pre>
  
  <p>and runs through the whole of that routine.</p>
  
  <p>My "Result" is a string and is thus Unicode. And my GetFirstLastName returns a string which is Unicode. No conversion of character set should be needed. </p>
  
  <p>I can't really tell what these System procedures are doing, but they are adding a lot of overhead to my routine. </p>
  
  <p>What are they doing? Are they necessary? If they aren't necessary, how can I prevent the compiler from calling those routines?</p>