3

我有记录,有关背景信息,请参阅此问题

TDigits = AnsiString;  //Should be `= array of NativeUInt`, but string has COW

TBigint = record
  Digit: TDigits; // Unsigned number, LSB stored in D[0], MSB in D[size-1]
  Size: Byte; // Mininum = 4, maximum 127.
  MSI: Byte; // Most significant (native)integer minimum=1, maximum=127
  Sign: Shortint;
  class operator Implicit(a: Integer): TBigint;

背景
我正在使用一个(几乎)像普通整数一样工作的 bignum 类。
因此a:= 1000000*10000000*12000000*10000000*1000000;会产生非常有用的结果。为此,我使用带有class operators. 这些触发自动类型转换和初始化。
除非没有转换,因为我将 a 分配TBigint给 another TBigint

解决方案
使用 Ansistring 存储核心数据,它具有写时复制功能,并会在需要时自行克隆。

问题:(如果 Delphi 不知道您正在更改字符串,则 COW 不起作用)
我有一些纯汇编程序例程可以操纵dynamic array伪装成Ansistring.

但是,当我做这样的事情时:

Label1.Caption:= BigintToStr(b);
..... this fires:

function BigintToStr(const X: TBigint): AnsiString;
var
  ..
  LocX:= x;   <<-- assignment, locX and X are joined at the hip.
  repeat
    D := DivBigint(LocX, 1000000000, LocX); <<-- this routine changes LocX
                                 ^^+-- but assembler routines bypass COW

X并且LocX在臀部关节处,无论一个发生在另一个身上。
显然 Delphi 不知道 asm 例程DivBigint正在改变LocX,因此 COW 是有序的。

解决方法
如果我将例程更改为:

function BigintToStr(const X: TBigint): AnsiString;
var
  ..
  LocX:= x;
  LocX.Digit[2]:= #0;  <<-- inconsequential change to force COW.
  repeat
    D := DivBigint(LocX, 1000000000, LocX);

德尔福得到了所有的线索并且表现得很好。 LocX并且X没有链接,一切正常。
但是,我不想在一些空白空间的中间做出愚蠢的改变。

是否有一种体面/适当/官方*的方式来强制触发字符串中的 COW?
可能是系统调用之类的东西?

*圈出你最喜欢的选项(用手绘圆圈)

4

2 回答 2

6

应该是评论,但需要更多空间...

如果你需要打电话UniqueString或同等的。
您不妨保留动态记录。

手册中的一段话:

在调用 SetLength 之后,S 保证引用一个唯一的字符串或数组——即引用计数为 1 的字符串或数组。如果没有足够的内存可用于重新分配变量,SetLength 将引发 EOutOfMemory 异常。

请注意,这种行为甚至在调用SetLength(Length(myArray));.
Delphi 会为您制作一份副本并解决问题。

所以事实证明,没有必要使用 AnsiStrings 毕竟,只要您在每个接受记录作为var参数的方法中调用 SetLength。

优点
这具有额外的好处,如果您调用SetLength扩展数组(经常发生),添加的空间将被零初始化。AnsiString 不会发生任何事情。

此外,无需担心大小转换,因为您array of TXYZ已经知道其元素的大小。使用 AnsiString 时,您需要到处添加* SizeOf(somestruct)

不需要类型转换,简化了代码;在调试器中,数据按设计显示。

仅调用 SetLength 即可生成动态数组 COW

如您所见,这两个实例不再链接。

于 2013-09-21T16:03:15.730 回答
4

每个改变缓冲区的方法都应该在执行修改之前调用UniqueString.

确保给定字符串的引用计数为 1。

事实上,这个细节是由Craig Young对我对你之前问题的回答的评论提供的。

如果您要使其可行,您将需要隐藏缓冲区。严格保密。这意味着您只能从您的记录方法中访问它。这样你就可以确定任何修改缓冲区的东西都会调用UniqueString.

就个人而言,我认为更好的解决方案是使类型不可变。

于 2013-09-21T13:38:06.800 回答