Peter N Lewis wrote:
And while I appreciate the elegance of the New( s, size ) approach, I'd still be interested to know how, or if, it is possible, at run time, to pack together GPC schema strings in a block of allocated memory, such that:
a) there is not much wasted memory (ie, wasting SizeOf(Integer) is fine, wasting 200+ bytes for a short string is not so good).
The latter never happens AFAIK, unless you allocate a string of a fixed "maximum" size.
b) the strings can be accessed directly (with ^String())
New is very clean, but it has a number of disadvantages:
a) Memory managers do not always work well with vast numbers of small allocations.
Do you have details? Of course, it depends on waht you mean by "not always". There probably are some better and some worse, but do you know which systems have bad ones? Fixing the MM would be a much more general solution than a hand-made solution for Pascal, strings, if explicitly used ...
Of course, any solution should only be implemented after profiling demonstrates that the simple clean solution is an expensive bottleneck - I'd just like to know if there is a way to pack Schema strings together to have that as an an option. Then I can compare and contrast the expense.
You can, of course, always create your pointers whichever way you like, taking care of alignment and sizes yourself. To get the size, you can use `SizeOf'. However, since it requires a type, or expressions of this type, it's a little more tricky to use here. This is one solution, perhaps the easiest one (pass the length of the string to be created).
function SizeOfString (Length: Integer): SizeType; type t = String (Length); begin SizeOfString := SizeOf (t) end;
But there's another problem. The string (in particular, the capacity) must be initialized. `New' does this automatically. You can call `Initialize (MyNewPointer^)', but for this to work, the pointer must know the capacity. So it must be declared as such. The following should work. In this example, you can replace the `GetMem' call with anything that points p to a properly aligned memory block of the given size.
program StringNewDemo;
type PString = ^String;
function StringNew (Length: Integer): PString; type t = String (Length); var p: ^t; begin GetMem (p, SizeOf (p^)); Initialize (p^); StringNew := p end;
var p: PString;
begin p := StringNew (7); p^ := 'foo bar'; WriteLn (p^) end.
Alternatively, you can hook into GPC's memory allocation. See GetMemPtr, FreeMemPtr, ReAllocMemPtr in gpc.pas. This way you can redirect *all* de-/allocations, but you can swap these routine pointers any time, to restrict it to certain pointers. (Or, e.g., you could check the requested size, handle small allocations yourself and pass larger to the original routines, which you saved before hooking the pointers. This way, you can handle not only strings, but all smaller allocations. This may or may not be what you want.)
When doing this, you can then allocate your strings simply with `New'.
Note that memory allocated differently must not be freed via the default routines, i.e. either never `Dispose' your strings at all, or hook FreeMemPtr as well. Also note that some library routines may de-/allocate memory, so to avoid confusion better make sure de-/allocation is either always or never hooked while library functions may be called.
Frank