@@ -150,11 +150,11 @@ void MunmapSegment(void* context, void* data, size_t size) {
150150}
151151} // namespace
152152
153- Result<FreeableBuffer> MmapDataLoader::load (
154- size_t offset,
155- size_t size,
156- ET_UNUSED const DataLoader::SegmentInfo& segment_info ) const {
157- ET_CHECK_OR_RETURN_ERROR (
153+ /* *
154+ * Helper for input validation.
155+ */
156+ Error MmapDataLoader::validate_input ( size_t offset, size_t size ) const {
157+ ET_CHECK_OR_RETURN_ERROR (
158158 // Probably had its value moved to another instance.
159159 fd_ >= 0 ,
160160 InvalidState,
@@ -173,6 +173,19 @@ Result<FreeableBuffer> MmapDataLoader::load(
173173 InvalidArgument,
174174 " Offset %zu too large for off_t" ,
175175 offset);
176+ return Error::Ok;
177+ }
178+
179+ Result<FreeableBuffer> MmapDataLoader::load (
180+ size_t offset,
181+ size_t size,
182+ ET_UNUSED const DataLoader::SegmentInfo& segment_info) const {
183+
184+ // Validate input.
185+ auto err = validate_input (offset, size);
186+ if (err != Error::Ok) {
187+ return err;
188+ }
176189
177190 // mmap() will fail if the size is zero.
178191 if (size == 0 ) {
@@ -268,5 +281,101 @@ Result<size_t> MmapDataLoader::size() const {
268281 return file_size_;
269282}
270283
284+ Error MmapDataLoader::load_into (
285+ size_t offset,
286+ size_t size,
287+ ET_UNUSED const SegmentInfo& segment_info,
288+ void * buffer) const {
289+
290+ ET_CHECK_OR_RETURN_ERROR (buffer != nullptr , InvalidArgument, " Buffer is null" );
291+
292+ // Validate input.
293+ auto err = validate_input (offset, size);
294+ if (err != Error::Ok) {
295+ return err;
296+ }
297+
298+ // Nothing to copy
299+ if (size == 0 ) {
300+ return Error::Ok;
301+ }
302+
303+ // Find the range of pages that covers the requested region.
304+ Range range =
305+ get_overlapping_pages (static_cast <uintptr_t >(offset), size, page_size_);
306+
307+ size_t map_size = range.size ;
308+ if (range.start + map_size > file_size_) {
309+ // Clamp to the end of the file.
310+ //
311+ // The Windows implementation of mmap uses CreateFileMapping which returns
312+ // error STATUS_SECTION_TOO_BIG (0xc0000040) if we try to map past the end
313+ // of the last page of a file mapped in as read-only.
314+ map_size = file_size_ - range.start ;
315+ }
316+
317+ // Map the pages read-only. MAP_PRIVATE vs. MAP_SHARED doesn't matter since
318+ // the data is read-only, but use PRIVATE just to further avoid accidentally
319+ // modifying the file.
320+ void * pages = ::mmap (
321+ nullptr ,
322+ map_size,
323+ PROT_READ,
324+ MAP_PRIVATE,
325+ fd_,
326+ static_cast <off_t >(range.start ));
327+ ET_CHECK_OR_RETURN_ERROR (
328+ pages != MAP_FAILED,
329+ AccessFailed,
330+ " Failed to map %s: mmap(..., size=%zd, ..., fd=%d, offset=0x%zx)" ,
331+ file_name_,
332+ range.size ,
333+ fd_,
334+ range.start );
335+
336+ if (mlock_config_ == MlockConfig::UseMlock ||
337+ mlock_config_ == MlockConfig::UseMlockIgnoreErrors) {
338+ int err = ::mlock (pages, size);
339+ if (err < 0 ) {
340+ if (mlock_config_ == MlockConfig::UseMlockIgnoreErrors) {
341+ ET_LOG (
342+ Debug,
343+ " Ignoring mlock error for file %s (off=0x%zd): "
344+ " mlock(%p, %zu) failed: %s (%d)" ,
345+ file_name_,
346+ offset,
347+ pages,
348+ size,
349+ ::strerror (errno),
350+ errno);
351+ } else {
352+ ET_LOG (
353+ Error,
354+ " File %s (off=0x%zd): mlock(%p, %zu) failed: %s (%d)" ,
355+ file_name_,
356+ offset,
357+ pages,
358+ size,
359+ ::strerror (errno),
360+ errno);
361+ ::munmap (pages, size);
362+ return Error::NotSupported;
363+ }
364+ }
365+ // No need to keep track of this. munmap() will unlock as a side effect.
366+ }
367+
368+ // Offset into mapped region.
369+ const size_t map_delta = offset - range.start ;
370+
371+ // Copy data into caller's buffer.
372+ std::memcpy (buffer, static_cast <uint8_t *>(pages) + map_delta, size);
373+
374+ // Unmap mapped region.
375+ ::munmap (pages, map_size);
376+
377+ return Error::Ok;
378+ }
379+
271380} // namespace extension
272381} // namespace executorch
0 commit comments