1// 2// ModelAsset.cpp 3// 4// Copyright © 2024 Apple Inc. All rights reserved. 5// 6// Please refer to the license found in the LICENSE file in the root directory of the source tree. 7 8 9#import <asset.h> 10 11#import <optional> 12 13#import <objc_safe_cast.h> 14 15namespace { 16 17NSNumber * _Nullable is_regular_file(NSURL *url, NSError * __autoreleasing *error) { 18 NSNumber *result = nil; 19 if (![url getResourceValue:&result forKey:NSURLIsRegularFileKey error:error]) { 20 return nil; 21 } 22 23 return SAFE_CAST(result, NSNumber); 24} 25 26NSNumber * _Nullable get_total_allocated_file_size(NSURL *url, NSError * __autoreleasing *error) { 27 NSNumber *result = nil; 28 if (![url getResourceValue:&result forKey:NSURLTotalFileAllocatedSizeKey error:error]) { 29 return nil; 30 } 31 32 return SAFE_CAST(result, NSNumber); 33} 34 35NSNumber * _Nullable get_allocated_file_size(NSURL *url, NSError * __autoreleasing *error) { 36 NSNumber *result = nil; 37 if (![url getResourceValue:&result forKey:NSURLFileAllocatedSizeKey error:error]) { 38 return nil; 39 } 40 41 return SAFE_CAST(result, NSNumber); 42} 43 44NSDate * _Nullable get_modification_date(NSURL *url, NSError * __autoreleasing *error) { 45 NSDate *result = nil; 46 if (![url getResourceValue:&result forKey:NSURLContentModificationDateKey error:error]) { 47 return nil; 48 } 49 50 return SAFE_CAST(result, NSDate); 51} 52 53void set_error_from_local_error( NSError * __autoreleasing *error, NSError *local_error) { 54 if (local_error && error) { 55 *error = local_error; 56 } 57} 58 59std::optional<executorchcoreml::PackageInfo> 60get_package_info(NSURL *directory_url, NSFileManager *fm, NSError * __autoreleasing *error) { 61 NSArray<NSString *> *properties = @[NSURLIsRegularFileKey, NSURLFileAllocatedSizeKey, NSURLTotalFileAllocatedSizeKey]; 62 63 __block NSError *local_error = nil; 64 BOOL (^errorHandler)(NSURL *url, NSError *error) = ^BOOL(NSURL *url, NSError *enumeration_error) { 65 local_error = enumeration_error; 66 return NO; 67 }; 68 69 NSDirectoryEnumerator *enumerator = [fm enumeratorAtURL:directory_url 70 includingPropertiesForKeys:properties 71 options:NSDirectoryEnumerationProducesRelativePathURLs 72 errorHandler:errorHandler]; 73 74 auto result = executorchcoreml::PackageInfo {.name = directory_url.lastPathComponent.UTF8String}; 75 for (NSURL *item_url in enumerator) { 76 if (local_error != nil) { 77 set_error_from_local_error(error, local_error); 78 return std::nullopt; 79 } 80 81 NSURL *file_url = [NSURL fileURLWithPath:item_url.path relativeToURL:directory_url]; 82 if (!file_url) { 83 continue; 84 } 85 86 if (!is_regular_file(file_url, &local_error).boolValue) { 87 continue; 88 } 89 90 NSNumber *size = get_total_allocated_file_size(file_url, &local_error) ?: get_allocated_file_size(file_url, &local_error); 91 if (!size) { 92 break; 93 } 94 95 NSDate *last_modification_date = get_modification_date(file_url, &local_error); 96 if (!last_modification_date) { 97 break; 98 } 99 100 int64_t last_modification_time_interval = static_cast<int64_t>(last_modification_date.timeIntervalSince1970 * 1000); 101 auto fileInfo = executorchcoreml::FileInfo { 102 .relative_path = std::string(item_url.relativePath.UTF8String), 103 .size_in_bytes = size.unsignedLongLongValue, 104 .last_modification_time_interval = last_modification_time_interval 105 }; 106 107 result.file_infos.emplace_back(std::move(fileInfo)); 108 } 109 110 if (local_error) { 111 set_error_from_local_error(error, local_error); 112 return std::nullopt; 113 } 114 115 return result; 116} 117 118} 119 120namespace executorchcoreml { 121 122std::optional<Asset> Asset::make(NSURL *url, 123 NSString *identifier, 124 NSFileManager *fm, 125 NSError * __autoreleasing *error) { 126 auto package_info = get_package_info(url, fm, error); 127 if (!package_info) { 128 return std::nullopt; 129 } 130 131 return Asset(identifier.UTF8String, url.path.UTF8String, std::move(package_info.value())); 132} 133} 134