I want to ask about this before I send a PR or two, because I'm not exactly confident what's happening here.
Let's talk about
ContiguousArrayBuffer first, because it's the simplest. In
init, we have
_storage = Builtin.allocWithTailElems_1( _ContiguousArrayStorage<Element>.self, realMinimumCapacity._builtinWordValue, Element.self) let storageAddr = UnsafeMutableRawPointer(Builtin.bridgeToRawPointer(_storage)) let endAddr = storageAddr + _swift_stdlib_malloc_size(storageAddr) let realCapacity = endAddr.assumingMemoryBound(to: Element.self) - firstElementAddress _initStorageHeader( count: uninitializedCount, capacity: realCapacity)
Now, the use of
malloc_size here is somewhat objectionable, since when porting, not all platforms are going to support this, and its use, e.g., in Linux, is described as "for debugging and introspection." These uses of
malloc_size also feels questionable since we have abstracted memory allocation with
Builtin.allocWithTailElems but then we do an end-run around SIL here and ask the system what it's done, assuming that
allocWithTailElems's allocator is going to be the same as the system's, and that raises more questions than answers.
But in fact, it seems like this is all unnecessary. I would assume that
Builtin.allocWithTailElems will always allocate at least
realMinimumCapacity * MemoryLayout<Element>.stride bytes in addition to the memory required for the thing itself (that is, type paraphrasing,
firstElementAddress - storageAddr).
So am I missing something, or why shouldn't we just
let realCapacity = realMinimumCapacity? Sure, the system allocator may allocate you more memory than you need, but it ought not allocate you less than you required, and
malloc_usable_size(3) for example says "Although the excess bytes can be overwritten by the application without ill effects, this is not good programming practice: the number of excess bytes in an allocation depends on the underlying implementation."
But even that's the easy case;
ManagedBuffer is far trickier. If you don't have
malloc_size afforded to you by the platform, to avoid invoking it, the simplest solution is to track allocations somewhere in a global, static context -- map from pointers to sizes whenever
allocWithTailElems is called. Either that, or
Header needs to obey some protocol/contract about storing capacity -- which would require an evolution process, because it's public API -- or track the tail-allocated capacity as a separate field, but
ManagedBufferPointer is rather aggressive about what stored properties exist alongside everything (c.f.
_checkValidBufferClass). My Swift-fu is not yet strong enough to provide strong answers these questions, I'm afraid...