NSData+DOUAudioMappedFile.m 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /* vim: set ft=objc fenc=utf-8 sw=2 ts=2 et: */
  2. /*
  3. * DOUAudioStreamer - A Core Audio based streaming audio player for iOS/Mac:
  4. *
  5. * https://github.com/douban/DOUAudioStreamer
  6. *
  7. * Copyright 2013-2016 Douban Inc. All rights reserved.
  8. *
  9. * Use and distribution licensed under the BSD license. See
  10. * the LICENSE file for full text.
  11. *
  12. * Authors:
  13. * Chongyu Zhu <i@lembacon.com>
  14. *
  15. */
  16. #import "NSData+DOUAudioMappedFile.h"
  17. #include <sys/types.h>
  18. #include <sys/mman.h>
  19. static NSMutableDictionary *get_size_map()
  20. {
  21. static NSMutableDictionary *map = nil;
  22. static dispatch_once_t onceToken;
  23. dispatch_once(&onceToken, ^{
  24. map = [[NSMutableDictionary alloc] init];
  25. });
  26. return map;
  27. }
  28. static void mmap_deallocate(void *ptr, void *info)
  29. {
  30. NSNumber *key = [NSNumber numberWithUnsignedLongLong:(uintptr_t)ptr];
  31. NSNumber *fileSize = nil;
  32. NSMutableDictionary *sizeMap = get_size_map();
  33. @synchronized(sizeMap) {
  34. fileSize = [sizeMap objectForKey:key];
  35. [sizeMap removeObjectForKey:key];
  36. }
  37. size_t size = (size_t)[fileSize unsignedLongLongValue];
  38. munmap(ptr, size);
  39. }
  40. static CFAllocatorRef get_mmap_deallocator()
  41. {
  42. static CFAllocatorRef deallocator = NULL;
  43. static dispatch_once_t onceToken;
  44. dispatch_once(&onceToken, ^{
  45. CFAllocatorContext context;
  46. bzero(&context, sizeof(context));
  47. context.deallocate = mmap_deallocate;
  48. deallocator = CFAllocatorCreate(kCFAllocatorDefault, &context);
  49. });
  50. return deallocator;
  51. }
  52. @implementation NSData (DOUAudioMappedFile)
  53. + (instancetype)dou_dataWithMappedContentsOfFile:(NSString *)path
  54. {
  55. return [[self class] _dou_dataWithMappedContentsOfFile:path modifiable:NO];
  56. }
  57. + (instancetype)dou_dataWithMappedContentsOfURL:(NSURL *)url
  58. {
  59. return [[self class] dou_dataWithMappedContentsOfFile:[url path]];
  60. }
  61. + (instancetype)dou_modifiableDataWithMappedContentsOfFile:(NSString *)path
  62. {
  63. return [[self class] _dou_dataWithMappedContentsOfFile:path modifiable:YES];
  64. }
  65. + (instancetype)dou_modifiableDataWithMappedContentsOfURL:(NSURL *)url
  66. {
  67. return [[self class] dou_modifiableDataWithMappedContentsOfFile:[url path]];
  68. }
  69. + (instancetype)_dou_dataWithMappedContentsOfFile:(NSString *)path modifiable:(BOOL)modifiable
  70. {
  71. NSFileHandle *fileHandle = nil;
  72. if (modifiable) {
  73. fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:path];
  74. }
  75. else {
  76. fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
  77. }
  78. if (fileHandle == nil) {
  79. return nil;
  80. }
  81. int fd = [fileHandle fileDescriptor];
  82. if (fd < 0) {
  83. return nil;
  84. }
  85. off_t size = lseek(fd, 0, SEEK_END);
  86. if (size < 0) {
  87. return nil;
  88. }
  89. int protection = PROT_READ;
  90. if (modifiable) {
  91. protection |= PROT_WRITE;
  92. }
  93. void *address = mmap(NULL, (size_t)size, protection, MAP_FILE | MAP_SHARED, fd, 0);
  94. if (address == MAP_FAILED) {
  95. return nil;
  96. }
  97. NSMutableDictionary *sizeMap = get_size_map();
  98. @synchronized(sizeMap) {
  99. [sizeMap setObject:[NSNumber numberWithUnsignedLongLong:(unsigned long long)size]
  100. forKey:[NSNumber numberWithUnsignedLongLong:(uintptr_t)address]];
  101. }
  102. return CFBridgingRelease(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)address, (CFIndex)size, get_mmap_deallocator()));
  103. }
  104. - (void)dou_synchronizeMappedFile
  105. {
  106. NSNumber *key = [NSNumber numberWithUnsignedLongLong:(uintptr_t)[self bytes]];
  107. NSNumber *fileSize = nil;
  108. NSMutableDictionary *sizeMap = get_size_map();
  109. @synchronized(sizeMap) {
  110. fileSize = [sizeMap objectForKey:key];
  111. }
  112. if (fileSize == nil) {
  113. return;
  114. }
  115. size_t size = (size_t)[fileSize unsignedLongLongValue];
  116. msync((void *)[self bytes], size, MS_SYNC | MS_INVALIDATE);
  117. }
  118. @end