Domanda

I've got a record, see this question for background info.

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;

Background
I'm using a bignum class that (almost) works like normal integers.
So a:= 1000000*10000000*12000000*10000000*1000000; yields perfectly useful results. For this purpose I use a record with class operators. These trigger automatic type conversion and initialisation.
Except when there is no conversion, because I'm assigning a TBigint to another TBigint.

The solution
Use an Ansistring to store the core data, it's got copy-on-write and will clone itself when needed.

The problem: (COW does not work if Delphi does not know you're altering the string)
I've got a few pure assembler routines that manipulate the Digit dynamic array disguised as Ansistring.

However when I do something like this:

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 and LocX are joint at the hip, whatever happens to one happens to the other.
Clearly Delphi does not know that the asm routine DivBigint is changing LocX and therefore a COW is in order.

The workaround
If I change the routine to:

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

Delphi gets all clued up and performs just fine. LocX and X are unlinked and everything works fine.
However I don't want to be making silly changes in the middle of some empty space.

Is there a decent/proper/offical* way to force trigger COW in strings?
Something like a system call perhaps?

*circle your favourite option (with a handdrawn circle)

È stato utile?

Soluzione 2

Every method that mutates the buffer should, before performing the modification, call UniqueString.

Ensures that a given string has a reference count of one.

In fact, this detail was supplied by Craig Young's comment to my answer to your earlier question.

If you are going to make this viable you are going to need to hide the buffer. Make it strict private. That means that you can only access it from methods of your record. And that way you can be sure that anything that modifies the buffer will call UniqueString.

Personally, I think that a better solution would be to make the type immutable.

Altri suggerimenti

Should be a comment, but need more space...

If you need to call UniqueString or equivalent.
You might as well retain the dynamic record.

A quote from the manual:

Following a call to SetLength, S is guaranteed to reference a unique string or array -- that is, a string or array with a reference count of one. If there is not enough memory available to reallocate the variable, SetLength raises an EOutOfMemory exception.

Note that this behavior even applies when calling SetLength(Length(myArray));.
Delphi will make a copy for you and untangle the problem.

So it turns out there is no need for the complication with the AnsiStrings After all, as long as you call SetLength in every method that accepts the record as a var parameter.

Advantages
This has the added benefit that if you call SetLength to expand the array (as happens often) the added space will be zero-initialized. Something does does not happen with the AnsiString.

Furthermore there is no need to bother with size translations because your array of TXYZ already knows the size of its elements. When using AnsiString you need to add * SizeOf(somestruct) all over the place.

No typecasting is needed, simplifying the code; and in the debugger the data shows up as it is designed.

A mere call to SetLength makes the dynamic array COW

As you can see the two instances are no longer linked.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top