在分析期间,我遇到了一个耗费了相当多时间的函数,但基本上归结为这段非常简单的代码:
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
Result := Copy(AInput, AStart, ASubstringLength);
end;
此函数返回预期的子字符串,但对于较长的输入,它不能很好地扩展。我查看了CPU视图中的汇编程序代码,从我所知道的(我通常不在汇编程序级别工作),似乎AInput
在调用Copy
之前被隐式转换为字符串
但是,由于此时字符串/字符数组的长度未知,因此转换代码必须遍历PChar
的长度,直到找到空终止符。这可以解释较长输入的可怕缩放。
但是,由于调用者传递PChar
的长度,我最初认为可以将方法转换为使用SetString
。
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
SetString(Result, AInput + AStart - 1, ASubstringLength);
end;
除SetString
工作从零开始(不是一个基于副本),在验证其输入方面似乎还有许多其他小事Copy
,而不是全部记录的内容(例如,任何小于1的起始值都会更改为1)。所以上面的天真实现并不总是像原始实现一样。
我的目标是尽可能复制Copy
例程,因为此函数是库的一部分,并且已被我的同事广泛使用。
我想知道以下实现是否实现了这一点,或者我是否需要了解Copy
的任何其他警告。注意:FLength
是AInput
的实际长度,它来自该函数所属模块中的另一部分。我为此示例删除了其他部分。
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
if (AInput = nil) then begin
Result := '';
end else begin
if (AStart < 1) then begin
AStart := 0;
end else begin
AStart := AStart - 1;
end;
if (ASubstringLength + AStart > FLength) then begin
ASubstringLength := FLength - AStart;
end;
SetString(Result, AInput + AStart, ASubstringLength);
end;
end;
我正在使用Delphi 2006,但我认为这在产品的其他版本(至少是非Unicode版本)中没有太大的不同。
答案 0 :(得分:5)
让我们考虑角落案例。我认为他们是:
AInput
无效。AStart < 1
。AStart > FLength
。ASubstringLength < 0
。ASubstringLength + (AStart-1) > FLength
。我认为我们可以忽略案例1。应该在呼叫者身上有责任提供有效的PChar
。事实上,由于AInput <> nil
不是有效的nil
,因此PChar
已经在我看来已经过了一步,我的检查已经过了一步。
其余的你已经覆盖2和5,但不是3和4.因此,如果用户提供的AStart
值太大,那么你将读取字符串的结尾。同样,用户可以轻松提供否定ASubstringLength
。我不认为你需要任何人编写代码来检查这些案例,因为你显然非常称职。
现在,如果你真的关心最后一滴性能,你不应该检查这些情况。要求用户传递有效参数。在调试模式下,使用{$IFOPF D+}
或Assert
可以检查输入。当然,如果这些论点来自外部来源,那么它们应该得到验证。
答案 1 :(得分:0)
不应将PChar
转换为string
,而应尝试将内容AInput + (AStart * SizeOf(PChar))
的内容从ASubstringLength * SizeOf(PChar)
复制到@Result
,因为它是&#39 ;更容易处理Result
作为指针。
Move
程序可以做到这一点。