|
Sounds simple doesn't. What could be more basic than:
stock := 'Yahoo'.
In traditional Smalltalk a string contains the length of the string in
the object header and thus allocates only that length. This approach is
fine for Smalltalk itself but creates an annoyance when this string needs
to be passed to an external call. For example:
WINAPI strlen: string basicAddress
In in STMT this passes a string to the C function 'string length' (strlen)
and the C function will assume that the string is null terminated.
Traditional Smalltalks
need to allocate a new string with an extra null byte before passing this
new string to an API call.
STMT on the other hand always allocates an extra hidden byte which contains the null
character. Thus the literal above will occupy 6 bytes where byte 6 contains zero.
You can demonstrate this in a couple of ways:
1. stock _sizeInBytes will return 6. Note the underscore which
indicates an inlined or primitive method.
2 stock _byteAt: 6 will return zero (the value of the hidden byte).
Again note the underscore as _byteAt: is inlined by the compiler.
You can thus pass STMT strings directly to a Windows API. Note the use of basicAddress
above. If you have looked carefully in the image you will see that strings are
automatically sent _asCinteger before being passed to an API. So you may
say "Why do I need to send basicAddress to the string since _asCinteger
automatically does that for me?". Good question and the answer is "it
is a question of performance". Passing the string directly to the API
involves a message send (_asCinteger). Passing the strings address (basicAddress)
to the API internally just pushes the address on the stack prior to the API call
which is a very quick operation.
STMT allows you to allocate strings in four different ways as shown in
the table below.
| Literal string |
e.g. string := 'My literal string'. |
| Initial state of bytes |
literal characters occupy bytes. Hidden byte set
to zero |
| Garbage collection |
None (owned by method) |
| Allocated in the method. |
| Write-protected in generated
executables. In general, the contents of a literal string should not be
changed. |
| Allocated string |
e.g. string := String new: 9. |
| Number of bytes allocated |
1 greater than the length |
| Initial state of bytes |
All bytes set to zero |
| Garbage collection |
Scanned and collected when references drop to
zero |
| Allocated in object memory. |
| Fine for usage in instance
variables. For methods, consider using localNew: to take advantage of
stack speed. |
| Stack string |
e.g. string := String localNew: 9. |
| Number of bytes allocated |
1 greater than the length |
| Initial state of bytes |
All bytes are uninitialized (i.e. contents
unknown) |
| Garbage collection |
Not scanned. Removed from stack when method
exits |
| Allocated on the stack which is as
fast as you can get. Before
passing this string to an external call the string must either be reset
e.g. string reset which sets all bytes to zero or a zero must be
placed in the last allocated byte e.g. string _byteAt: 10 put: 0 |
| Care must be taken not to use this
string outside of its scope or a protection violation will occur (since
the string no longer exists on the stack). Do not try to use localNew: in
a workspace for example and show the result. |
| Heap string |
e.g. string := String heapAlloc: 9. |
| Number of bytes allocated |
1 greater than the length |
| Initial state of bytes |
All bytes set to zero |
| Garbage collection |
Not scanned. Removal must be done by user e.g.
string heapFree |
| Allocated on the default process heap. |
| Very useful if the object model is static. For example if you need to load up large
numbers of strings that will never need to be garbage collected or you can
explicitly deallocate them (using heapFree). |
|