Repository navigation
API Spec: Add zero-copy accessor ResourceCandidate.ValueAsMemoryBuffer for EmbeddedData - #6659
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
| /// Zero-copy accessor for embedded/binary resources: returns a read-only IMemoryBuffer that | ||
| /// projects the resource bytes directly (no copy). The buffer (and any reference created | ||
| /// from it) pins the backing resource data for its lifetime; Close it when done. Throws for | ||
| /// Kind != EmbeddedData. Prefer this over ValueAsBytes to avoid a per-call copy. |
There was a problem hiding this comment.
Why must this throw on "non-embedded data"? Maybe the app has downstream stuff that works better with IMemoryBuffer so "get an IMemoryBuffer" seems nicer. You have all the logic already - if somebody calls this on non-embedded-data value, conjure up a new MemoryBuffer filled with ValueAsBytes... Avoids more conditionals in caller code.
There was a problem hiding this comment.
DISCUSSION: Current API returning as BYTEs requires it to be embedded and throws if it isn't. Docs don't say this but code does. Throwing here maintains consistency with existing behavior.
RECOMMEND: Doc bug; fix
There was a problem hiding this comment.
I guess my point was that the consistency is needless. This is a new API, returning a view on data. Why can't it "just work" on all kinds of resource data that has a linear byte representation? Looser rules are usually good. More use cases are usually good.
Howard Kapustein (DrusTheAxe)
left a comment
There was a problem hiding this comment.
API Review: APPROVED
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). 2 pipeline(s) were filtered out due to trigger conditions. |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). 2 pipeline(s) were filtered out due to trigger conditions. |
…r for EmbeddedData (#6660) Implements #6659. # Summary Loading an embedded (`EmbeddedData`) resource from a memory-mapped PRI previously copied the same blob up to three times: twice at load and once more on every read. This change removes all three copies so the bytes can be read directly from the read-only PRI mapping. ## The copies and how each is removed ### Copy 1: load-time heap duplication The loaders (`MRM.cpp`) obtained a `BlobResult` that was already just a *view* into the mapped PRI, then forced a heap copy via `BlobResultReleaseOwnershipBuffer` purely to decouple the returned bytes from the mapping lifetime. **Fix:** New opt-in loaders `MrmLoadStringOrEmbeddedResourceNoCopy` / `...ByIndexNoCopy` return the view directly (`MrmResourceData2.isView == TRUE`) instead of copying. Lifetime is preserved by holding a strong resource-manager reference so the mapping stays alive for the candidate's lifetime. Owned buffers (e.g. decompressed blobs) still come back with `isView == FALSE`. ### Copy 2: construction `ResourceMap.cpp` wrapped the load-time buffer in an `array_view` which the `ResourceCandidate` ctor copied *again* into a `com_array m_blobData`. The original was then released, so it was a transient allocation with an unnecessary memcopy of the entire buffer. **Fix:** Switch to an adopting constructor, where `ResourceCandidate` now takes ownership of the loader buffer (`embedded_resoure_ptr`) rather than re-copying it. For the no-copy (`isView==TRUE`) path, it simply stores the borrowed view. ### Copy 3: every read `ValueAsBytes()` allocated a fresh `com_array` copy on each call. **Fix:** Added `ValueAsMemoryBuffer()` WinRT API, which returns an `IMemoryBuffer` over the single backing buffer/view. This enables zero copies per read. WinUI will be updated to use this API. (`ValueAsBytes()` is unchanged for compatibility.) ## ABI safety The existing APIs are unchanged. New flat-C APIs in `MRM.h` enable a non-copy view access to embedded data. The new `ResourceCandidate.ValueAsMemoryBuffer()` provides that access via the WinRT `ResourceCandidate` type. ## Testing New TAEF unit tests cover view vs. owned results, strings-are-not-views, by-index loading, and `MrmFreeResourceData` ownership semantics. The changes have also been tested in a WinUI app on a private build using the new `ValueAsMemoryBuffer()` API.
….ValueAsMemoryBuffer for EmbeddedData (#6857) * Implement new zero-copy accessor ResourceCandidate.ValueAsMemoryBuffer for EmbeddedData (#6660) Implements #6659. # Summary Loading an embedded (`EmbeddedData`) resource from a memory-mapped PRI previously copied the same blob up to three times: twice at load and once more on every read. This change removes all three copies so the bytes can be read directly from the read-only PRI mapping. ## The copies and how each is removed ### Copy 1: load-time heap duplication The loaders (`MRM.cpp`) obtained a `BlobResult` that was already just a *view* into the mapped PRI, then forced a heap copy via `BlobResultReleaseOwnershipBuffer` purely to decouple the returned bytes from the mapping lifetime. **Fix:** New opt-in loaders `MrmLoadStringOrEmbeddedResourceNoCopy` / `...ByIndexNoCopy` return the view directly (`MrmResourceData2.isView == TRUE`) instead of copying. Lifetime is preserved by holding a strong resource-manager reference so the mapping stays alive for the candidate's lifetime. Owned buffers (e.g. decompressed blobs) still come back with `isView == FALSE`. ### Copy 2: construction `ResourceMap.cpp` wrapped the load-time buffer in an `array_view` which the `ResourceCandidate` ctor copied *again* into a `com_array m_blobData`. The original was then released, so it was a transient allocation with an unnecessary memcopy of the entire buffer. **Fix:** Switch to an adopting constructor, where `ResourceCandidate` now takes ownership of the loader buffer (`embedded_resoure_ptr`) rather than re-copying it. For the no-copy (`isView==TRUE`) path, it simply stores the borrowed view. ### Copy 3: every read `ValueAsBytes()` allocated a fresh `com_array` copy on each call. **Fix:** Added `ValueAsMemoryBuffer()` WinRT API, which returns an `IMemoryBuffer` over the single backing buffer/view. This enables zero copies per read. WinUI will be updated to use this API. (`ValueAsBytes()` is unchanged for compatibility.) ## ABI safety The existing APIs are unchanged. New flat-C APIs in `MRM.h` enable a non-copy view access to embedded data. The new `ResourceCandidate.ValueAsMemoryBuffer()` provides that access via the WinRT `ResourceCandidate` type. ## Testing New TAEF unit tests cover view vs. owned results, strings-are-not-views, by-index loading, and `MrmFreeResourceData` ownership semantics. The changes have also been tested in a WinUI app on a private build using the new `ValueAsMemoryBuffer()` API. * Added containment
Spec proposes new zero-copy accessors for embedded MRT Core resources. Both existing paths copy the bytes on every read even though the
.priis already memory-mapped. The new APIs hand out a read-only, non-owning view instead.Covers two API layers:
ResourceCandidate.ValueAsMemoryBuffer(), anIMemoryBuffer-based alternative toValueAsBytes.MRM.h):MrmResourceData2,MrmLoadStringOrEmbeddedResourceNoCopy,MrmLoadStringOrEmbeddedResourceByIndexNoCopy, andMrmFreeResourceData.ResourceCandidate.ValueAsMemoryBuffer()is the important public API, expected to be used by WinUI and others. The flat-C APIs are support for ResourceCandidate.