iOS-底層原理40-Hmap使用(美團(tuán)文章分析)

《iOS底層原理文章匯總》

1.OC&Swift測(cè)試工程配置

I.在測(cè)試工程中測(cè)試主工程中代碼,直接引入頭文件進(jìn)行調(diào)用

image

II.編寫Swift測(cè)試用例

創(chuàng)建時(shí)點(diǎn)擊Create Bridging Header,此時(shí)依然無法使用ViewController類中的方法

image
image.png

在主工程中創(chuàng)建swift工程,讓系統(tǒng)生成橋接文件,在橋接文件中引入“ViewController.h”,此時(shí)還需在需要使用OC文件的地方引入主Module才能使用ViewController

image
image
image

Swift測(cè)試文件使用OC代碼的時(shí)候
A.主工程Bridge文件導(dǎo)出相關(guān)的頭文件
B.Swift測(cè)試文件引入主工程Module

TestAppTests-Bridging-Header.h是將測(cè)試工程中的OC文件給測(cè)試工程中的swift使用

TestApp-Bridging-Header.h是將主工程中的OC文件給其他swift代碼使用

在主工程swift文件中新建兩個(gè)module,在測(cè)試工程中如何使用呢?

image

swift沒有權(quán)限控制符的時(shí)候,默認(rèn)為不開放,此時(shí)用@testable import導(dǎo)入主工程公開權(quán)限,只是針對(duì)測(cè)試工程使用

image

若swift類使用public、open修飾,則import TestApp能訪問到

image

若沒有修飾或internal修飾,則需要使用@testable import TestApp

image.png

若在OC中使用swift model,并不能直接使用,需要引入系統(tǒng)幫我們默認(rèn)生成的頭文件,頭文件以當(dāng)前App名稱開頭-Swift.h

image

同理,若要在測(cè)試工程中使用swift相關(guān)的代碼,需要引入TestApp-Swift.h頭文件,然而TestApp和TestAppTests屬于兩個(gè)target,兩個(gè)不同的項(xiàng)目,TestApp系統(tǒng)默認(rèn)生成的TestApp-Swift.h,我們是直接訪問不到的,需要在header search path中指明路徑

image

TestApp-Swift.h在product目錄下,show in finder

image

Xcode會(huì)自動(dòng)生成一個(gè)環(huán)境變量$CONFIGURATION_TEMP_DIR,
指向如下目錄


image

TestApp-Swift.h所在的路徑為$CONFIGURATION_TEMP_DIR/TestApp.build/DerivedSources,放入TestAppTests工程的header search paths中

image

遞歸和不遞歸的區(qū)別是,遞歸情況下,會(huì)在給的
路徑下面的子文件夾中去尋找,若非遞歸狀態(tài)下
不會(huì)在子目錄下面去找,相當(dāng)于需要給絕對(duì)路徑

image

遞歸狀態(tài)下,配置絕對(duì)路徑

image.png

2.header search path底層是-I,接收兩個(gè)參數(shù),頭文件的目錄,指定hmap文件

hmap:key-value,為什么-I可以通過hmap解析到頭文件所在的目錄,這是美團(tuán)這篇文章的重點(diǎn)一款可以讓大型iOS工程編譯速度提升50%的工具

如何通過hmap加快編譯速度?

hmap是一個(gè)容器,包裹著頭文件所在的目錄,通過xcode去查找文件的時(shí)候更快速

hmap文件如何生成?底層結(jié)構(gòu)是什么樣的?

查看.m文件的編譯,發(fā)現(xiàn)在編譯期底層通過-I參數(shù)鏈接了-I/Users/cloud/Library/Developer/Xcode/DerivedData/TestApp-azujxtfyspnygtekfcgtbfaeptix/Build/Intermediates.noindex/TestApp.build/Debug-iphonesimulator/TestApp.build/TestApp-all-target-headers.hmap

image.png

來到這個(gè)目錄,發(fā)現(xiàn)有很多hmap文件

image

通過cat查看.hmap中的內(nèi)容,怎么理解.hmap底層到底是什么樣的數(shù)據(jù)結(jié)構(gòu)體呢?借助llvm工程查看.hmap的結(jié)構(gòu),hmap文件由HampHeader和HmapBucket,若target中包含3個(gè)頭文件,則會(huì)有3個(gè)HmapBucket,Bucket的數(shù)量與頭文件的數(shù)量相等,
通過讀取Hmapheader,知道當(dāng)前二進(jìn)制里面有多少個(gè)Bucket,Bucket表示當(dāng)前的hmap里面保存了多少個(gè)頭文件的路徑,通過Bucket,底下保存了字符串,Bucket中有偏移量,可以根據(jù)偏移量計(jì)算出保存字符串的位置,從而讀取出頭文件的路徑

前半部分是配置文件,后半部分存儲(chǔ)真正的信息

image.png

怎么將文件讀取成結(jié)構(gòu)體,再讀取出頭文件的字符串呢?
在llvm中的LexTests target中我們發(fā)現(xiàn)了結(jié)構(gòu)體HMapBucket和HMapHeader,同時(shí)發(fā)現(xiàn)將.hmap解析成HMapBucket和HMapHeader的源代碼

image.png
image

通過C函數(shù)讀取hmap文件,提取其中的HmapHeader和HmapBucket,將二進(jìn)制轉(zhuǎn)化為結(jié)構(gòu)體,通過打印HMapBucket中的Key、Prefix、Suffix

struct HMapBucket {
  uint32_t Key;    // Offset (into strings) of key.
  uint32_t Prefix; // Offset (into strings) of value prefix.
  uint32_t Suffix; // Offset (into strings) of value suffix.
};
image
image.png
image

將前文生成的TestApp-project-headers.hmap拖入到讀取.hmap文件的根目錄下

image
image

Xcode配置的讀取.hmap文件路徑中不能有空格

image

將.hmap文件的路徑傳入main函數(shù)中,通過C語言和結(jié)構(gòu)體內(nèi)存平移原則,先讀取HmapHeader,再讀取HmapBucket
HMapHeader -> 指明了有多少個(gè)Bucket
每一個(gè)Bucket代表了一個(gè)頭文件string_table位置
字符串讀取出來:HMapBucket緊跟著HMapHeader
Bucket首地址:string_table首地址 = HMapHeader大小 + num *HMapBucket

void
dump(const char *path)
{
    // c來寫
    // OCSwift
    // hmap文件路徑
    int fd = open(path, O_RDONLY|O_CLOEXEC);
    if (fd < 0) {
        warn(/*EX_NOINPUT,*/ "%s: cannot open", path);
        return;
    }

    struct HMapHeader header;
    // 二進(jìn)制數(shù)據(jù)
    // sizeof(HMapHeader)
    // header -> Bucket-> 讀取Bucket表示的頭文件路徑
    ssize_t nread = read(fd, &header, sizeof(header));
    if (nread < 0) {
        warn(/*EX_IOERR,*/ "%s: failed to read header", path);
        (void)close(fd);
        return;
    } else if ((size_t)nread < sizeof(header)) {
        warn(
            /*EX_DATAERR,*/
            "%s: short read: expected %zu bytes, read only %zd",
            path, sizeof(header), nread);
        (void)close(fd);
        return;
    }
    bool is_swapped = false;
    uint32_t (*swap)(uint32_t) = nop_swap;
    if (header.Magic == HMAP_HeaderMagicNumber
        && header.Version == HMAP_HeaderVersion) {
        is_swapped = false;
    } else if (header.Magic == HMAP_SwappedMagic
        && header.Version == HMAP_SwappedVersion) {
        is_swapped = true;
        swap = actually_swap;
    } else {
        warn(/*EX_DATAERR,*/ "header lacks HMAP magic");
        (void)close(fd);
        return;
    }
    
    // 8 數(shù)據(jù)對(duì)齊
    const uint32_t bucket_count = swap(header.NumBuckets);
    printf("Header map: %s\n"
        "\tHash bucket count: %" PRIu32 "\n"
        "\tString table entry count: %" PRIu32 "\n"
        "\tMax value length: %" PRIu32 " bytes\n",
        path,
        bucket_count,
        swap(header.NumEntries),
        swap(header.MaxValueLength));

    struct stat stat;
    int stat_err = fstat(fd, &stat);
    if (stat_err) {
        warn("%s: fstat failed; cannot dump buckets", path);
        (void)close(fd);
        return;
    }

    off_t hmap_length = stat.st_size;
    const void *hmap = mmap(0, hmap_length, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0 /*offset*/);
    (void)close(fd);
    if (MAP_FAILED == hmap) {
        warn("%s: failed to mmap; cannot dump buckets", path);
        return;
    }
    const char *raw = (const char *)hmap;
    // mach header -》指明了有多少個(gè)load commands
    // HMapHeader -》指明了有多少個(gè)Bucket
    // 每一個(gè)Bucket代表了一個(gè)頭文件string_table位置
    // HMapBucket緊跟著HMapHeader
    // HMapBucket
    //
    // 字符串讀取出來
    
    // Bucket首地址
    // string_table首地址 = HMapHeader大小 + num *HMapBucket
    const struct HMapBucket *const buckets = (const struct HMapBucket *const)(raw
            + sizeof(struct HMapHeader));
    const char *const string_table = (raw
            + sizeof(struct HMapHeader)
            + bucket_count*sizeof(struct HMapBucket));

    // 2
    for (uint32_t i = 0; i < bucket_count; ++i) {
        const struct HMapBucket *const bucket = &(buckets[i]);
        if (swap(bucket->Key) == HMAP_EmptyBucketKey) { continue; }
//
        // LLVM is careful to sanity-check bucket-count and strlen,
        // but we're just going to assume they're all NUL-terminated
        // and not maliciously crafted input files.
        //
        // This is naive, but this is also not exactly "production" code.
        // string_table 位置
        // 首地址 + 位置
        const char *key = string_table + swap(bucket->Key);
        const char *prefix = string_table + swap(bucket->Prefix);
        const char *suffix = string_table + swap(bucket->Suffix);

        printf("\t- Bucket %" PRIu32 ": "
            "Key %s -> Prefix %s, Suffix %s\n",
            i,
            key, prefix, suffix);
    }
}
/**
 // 主題
 Key LGTestApp-Bridging-Header.h
 //頭文件的前半部分
Prefix /Users/ws/Desktop/VIP課程/第十六節(jié)課、單元測(cè)試與UI測(cè)試/上課代碼/01-OC&Swift測(cè)試工程配置/LGTestApp/LGTestApp/
 //頭文件的后半部分
 Suffix LGTestApp-Bridging-Header.h
 // hmap 對(duì)應(yīng) 按照規(guī)則存儲(chǔ)一堆的頭文件
 */

uint32_t
nop_swap(uint32_t u)
{
    return u;
}

uint32_t
actually_swap(uint32_t u)
{
    uint32_t n = (
        ((u & 0xFF) << 24)
        | ((u & 0xFF00) << 8)
        | ((u & 0xFF0000) >> 8)
        | ((u & 0xFF000000) >> 24)
        );
    return n;
}


int
main(int argc, const char *argv[])
{
    assert(actually_swap(HMAP_HeaderMagicNumber) == HMAP_SwappedMagic
        && "failed to properly swap things :(");

    // header map 路徑
    if (argc < 2) {
        fprintf(
            stderr,
            "usage: %s HMAP_FILE [HMAP_FILE...]\n\n"
            "Dump clang headermap (.hmap file) contents.\n\n"
            "See: https://github.com/llvm-mirror/clang/blob/release_40/include/clang/Lex/HeaderMapTypes.h\n"
            "and related files\n",
            getprogname());
        return EX_USAGE;
    }

    for (int i = 1; i < argc; ++i) {
        //導(dǎo)出 header map
        dump(argv[i]);
        putchar('\n');
    }
    return EXIT_SUCCESS;
}

讀取出來頭文件結(jié)果,包含工程中的5個(gè)頭文件,每個(gè)bucket由key、prefix、suffix組成,分別為工程中的TestApp-Bridging-Header.h,ViewController.h,AppDelegate.h,SceneDelegate.h,TestAppTests-Bridging-Header.h,并不說一定是8個(gè)bucket,存在數(shù)據(jù)對(duì)齊規(guī)則
key:表示主題
Prefix:表示頭文件的前半部分
Suffix:表示頭文件的后半部分
hmap對(duì)應(yīng)按照規(guī)則存儲(chǔ)的一堆頭文件

Header map: /Users/cloud/Documents/iOS/iOS強(qiáng)化班/LoginiOS高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/上課代碼/02-HeaderMap原理&讀取/DumpHeaderMap/TestApp-project-headers.hmap
    Hash bucket count: 8
    String table entry count: 5
    Max value length: 0 bytes
    - Bucket 0: Key TestApp-Bridging-Header.h -> Prefix /Users/cloud/Documents/iOS/iOS強(qiáng)化班/Login iOS 高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestApp/, Suffix TestApp-Bridging-Header.h
    - Bucket 1: Key ViewController.h -> Prefix /Users/cloud/Documents/iOS/iOS強(qiáng)化班/Login iOS 高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestApp/, Suffix ViewController.h
    - Bucket 2: Key AppDelegate.h -> Prefix /Users/cloud/Documents/iOS/iOS強(qiáng)化班/Login iOS 高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestApp/, Suffix AppDelegate.h
    - Bucket 3: Key SceneDelegate.h -> Prefix /Users/cloud/Documents/iOS/iOS強(qiáng)化班/Login iOS 高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestApp/, Suffix SceneDelegate.h
    - Bucket 7: Key TestAppTests-Bridging-Header.h -> Prefix /Users/cloud/Documents/iOS/iOS強(qiáng)化班/Login iOS 高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestAppTests/, Suffix TestAppTests-Bridging-Header.h
image

3.美團(tuán)的文章說生成的頭文件為n,生成的hmap文件為n+1,1代表1個(gè),n代表n個(gè)

-I鏈接頭文件可以接受兩種參數(shù),第一種是接收一個(gè)路徑,第二種是接收.hmap文件

兩種查找頭文件的方式

I.查找頭文件通過目錄的方式查找,如cocoapods自動(dòng)生成的debug.xcconfig文件

缺點(diǎn):若在多個(gè)目錄里面尋找一個(gè)頭文件,會(huì)比較耗時(shí),如果能把一個(gè)文件路徑提前放在一個(gè)hmap文件里面,去讀取文件里面的路徑會(huì)快很多,header search paths中有頭文件的數(shù)量限制的

若頭文件比較少,不需要用到.hmap文件,頭文件越少,.hmap的作用越不明顯,
頭文件數(shù)量越多越明顯

image.png

II.通過加載.hmap文件解析方式查找

4.通過終端直接查看.hmap文件里面的內(nèi)容

A.將DumpHeaderMap的可執(zhí)行文件復(fù)制到全局目錄路徑中,往shell配置文件中導(dǎo)出路徑,將可執(zhí)行文件路徑放入~/.bashrc中,執(zhí)行source ~/.bashrc,使環(huán)境變量生效

image.png
image
image

B.檢驗(yàn)DumpHeaderMap命令是否生效,發(fā)現(xiàn)已經(jīng)生效,此時(shí)頭文件的路徑均為絕對(duì)路徑,在什么情況下頭文件的路徑會(huì)是相對(duì)路徑呢?

image
image

在前文的工程中新建Headers Phase,將ViewController拖到Public目錄下,Cmd + B編譯后,得到新的.hmap文件,查看路徑,發(fā)現(xiàn)ViewController.h由絕對(duì)路徑變?yōu)榱讼鄬?duì)路徑

image.png
image.png
image

將ViewController.h拖入Project目錄下,此時(shí)編譯生成的.hmap文件里面的ViewController.h文件才是絕對(duì)路徑目錄

image
image

image

美團(tuán)文章里面,n+1中n表示私有頭文件(絕對(duì)路徑的可以放到一起),1表示公共頭文件(公有的頭文件放到一起)

5.怎么生成.hmap頭文件呢?

前文在測(cè)試工程中,使用swift相關(guān)代碼,需要引入TestApp-Swift.h頭文件,而測(cè)試工程和主工程屬于不同的target,要引入頭文件,需要在header search path中指明路徑,此時(shí)可以模擬生成一個(gè).hmap文件,包含TestApp-Swift.h頭文件來替換header search path中的路徑

I.生成包含TestApp-Swift.h頭文件的.hmap文件,將TestApp-Swift.h頭文件拷貝到工程SRCROOT目錄下

image

II.在配置工程中去插入Bucket,只配置一個(gè)TestApp-Swift.h,生成.mm文件中包含這個(gè)Bucket

image
image
image.png

III.在TestApp的測(cè)試工程TestAppTests的header search paths中指定包含TestApp-Swift.h文件的mm.hmap文件的路徑,此時(shí)發(fā)現(xiàn)仍舊找不到TestApp-Swift.h頭文件的路徑,原因是.hmap中包含的.h文件要全,當(dāng)前target工程中引用的其他的頭文件也要寫入mm.hmap中,ViewController.h和TestApp-Bridging-Header.h,均寫入mm.hmap文件中

image.png
image

新加ViewController.h和TestApp-Bridging-Header.h,均寫入mm.hmap文件中

image.png
image

重新運(yùn)行Cmd + U 進(jìn)行編譯,看工程是否能找到TestApp-Swift.h頭文件
此時(shí)仍然找不到TestApp-Swift.h頭文件

image

查看當(dāng)前項(xiàng)目TestApp中Xcode生成的TestApp-project-headers.hmap文件中包含的頭文件,發(fā)現(xiàn)有5個(gè)頭文件,繼續(xù)往.mm文件中添加TestAppTests-Bridging-Header.h頭文件

image

增加TestAppTests-Bridging-Header.h頭文件后,mm.hmap中的頭文件的數(shù)量變?yōu)?個(gè),此時(shí)編譯還是不通過,加大數(shù)字typedef MapFile<8, 750> FileTy;重新編譯

image
image

typedef MapFile<8, 750> FileTy,控制生成多少個(gè)NumBuckets,NumBytes

image

此時(shí)能build success,mm.hmap文件中寫入頭文件管用

image

生成.hmap源碼如下

#import <Foundation/Foundation.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <err.h>
#include <fcntl.h>
#include <stdbool.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string>
#include <malloc/malloc.h>

enum {
  HMAP_HeaderMagicNumber = ('h' << 24) | ('m' << 16) | ('a' << 8) | 'p',
  HMAP_HeaderVersion = 0x0001,
  HMAP_EmptyBucketKey = 0,

  HMAP_SwappedMagic = ('h' << 0) | ('m' << 8) | ('a' << 16) | ('p' << 24),
  HMAP_SwappedVersion = 0x0100,
};

struct HMapBucket {
  uint32_t Key;    // Offset (into strings) of key.
  uint32_t Prefix; // Offset (into strings) of value prefix.
  uint32_t Suffix; // Offset (into strings) of value suffix.
};

struct HMapHeader {
  uint32_t Magic;          // Magic word, also indicates byte order.
  uint16_t Version;        // Version number -- currently 1.
  uint16_t Reserved;       // Reserved for future use - zero for now.
  uint32_t StringsOffset;  // Offset to start of string pool.
  uint32_t NumEntries;     // Number of entries in the string table.
  uint32_t NumBuckets;     // Number of buckets (always a power of 2).
  uint32_t MaxValueLength; // Length of longest result path (excluding nul).
  // An array of 'NumBuckets' HMapBucket objects follows this header.
  // Strings follow the buckets, at StringsOffset.
};

void write();
uint32_t nop_swap(uint32_t);
uint32_t actually_swap(uint32_t);

// The header map hash function.
static inline unsigned getHash(NSString *Str) {
  return Str.hash;
}

inline uint32_t ByteSwap_32(uint32_t value) {
#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC))
  return __builtin_bswap32(value);
#elif defined(_MSC_VER) && !defined(_DEBUG)
  return _byteswap_ulong(value);
#else
  uint32_t Byte0 = value & 0x000000FF;
  uint32_t Byte1 = value & 0x0000FF00;
  uint32_t Byte2 = value & 0x00FF0000;
  uint32_t Byte3 = value & 0xFF000000;
  return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
#endif
}
inline uint16_t ByteSwap_16(uint16_t value) {
#if defined(_MSC_VER) && !defined(_DEBUG)
  // The DLL version of the runtime lacks these functions (bug!?), but in a
  // release build they're replaced with BSWAP instructions anyway.
  return _byteswap_ushort(value);
#else
  uint16_t Hi = value << 8;
  uint16_t Lo = value >> 8;
  return Hi | Lo;
#endif
}
inline unsigned short getSwappedBytes(unsigned short C) { return ByteSwap_16(C); }
inline   signed short getSwappedBytes(  signed short C) { return ByteSwap_16(C); }
inline unsigned int   getSwappedBytes(unsigned int   C) { return ByteSwap_32(C); }
inline   signed int   getSwappedBytes(  signed int   C) { return ByteSwap_32(C); }
template <class FileTy> struct FileMaker {
  FileTy &File;
  unsigned SI = 1;
  unsigned BI = 0;
  FileMaker(FileTy &File) : File(File) {}

 unsigned addString(std::string S) {
    assert(SI + S.size() + 1 <= sizeof(File.Bytes));
    
    std::copy(S.begin(), S.end(), File.Bytes + SI);
    auto OldSI = SI;
    SI += S.size() + 1;
    return OldSI;
  }
  void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, unsigned Suffix) {
      // 9 & 8
    assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1)));
    unsigned I = Hash & (File.Header.NumBuckets - 1);
    do {
      if (!File.Buckets[I].Key) {
        File.Buckets[I].Key = Key;
        File.Buckets[I].Prefix = Prefix;
        File.Buckets[I].Suffix = Suffix;
        ++File.Header.NumEntries;
        return;
      }
      ++I;
      I &= File.Header.NumBuckets - 1;
    } while (I != (Hash & (File.Header.NumBuckets - 1)));
  }
};

template <unsigned NumBuckets, unsigned NumBytes> struct MapFile {
  HMapHeader Header;
  HMapBucket Buckets[NumBuckets];
  unsigned char Bytes[NumBytes];

  void init() {
    memset(this, 0, sizeof(MapFile));
    Header.Magic = HMAP_HeaderMagicNumber;
    Header.Version = HMAP_HeaderVersion;
    Header.NumBuckets = NumBuckets;
    Header.StringsOffset = sizeof(Header) + sizeof(Buckets);
  }

  void swapBytes() {
//    using llvm::sys::getSwappedBytes;
    Header.Magic = getSwappedBytes(Header.Magic);
    Header.Version = getSwappedBytes(Header.Version);
    Header.NumBuckets = getSwappedBytes(Header.NumBuckets);
    Header.StringsOffset = getSwappedBytes(Header.StringsOffset);
  }

  NSData *getBuffer() const {
      const char *c = reinterpret_cast<const char *>(this);
      
      NSData *data = [NSMutableData dataWithBytes:c length:sizeof(MapFile)];
      
      return data;
  }
};


void write() {
    struct HMapHeader header;
    // 2 :幾個(gè)Bucket
    // 150: buffer大小
    // 生成的NumBuckets NumBytes
    // NumBuckets NumBytes有影響的
    // 研究
    // 腳本 -》 Cocoapods pub 實(shí)際是腳本表現(xiàn)形式之一
    // 最大的亮亮點(diǎn) 不是跑通 害怕
//    typedef MapFile<8, 750> FileTy;
//    typedef MapFile<2, 200> FileTy;
    typedef MapFile<8, 750> FileTy;
    FileTy File;
    File.init();
    //OC
    FileMaker<FileTy> Maker(File);
    // 原理性東西全部走通了
    // 1.Bucket
    //  key
    // xcode生成的
    // 原理 -》想象原理沒有錯(cuò)
    // iOS為什么難
    // Apple
    // hmap -》搜 -〉搜不出來啥
    auto a = Maker.addString("TestApp-Swift.h");
    // 前半部分
    auto b = Maker.addString("/Users/cloud/Documents/iOS/iOS強(qiáng)化班/LoginiOS高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/");
    // 后半部分
    auto c = Maker.addString("TestApp-Swift.h");
    Maker.addBucket(getHash(@"TestApp-Swift.h"), a, b, c);
    
    
    auto ViewControllera = Maker.addString("ViewController.h");
    auto ViewControllerb = Maker.addString("/Users/cloud/Documents/iOS/iOS強(qiáng)化班/LoginiOS高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestApp/");
    auto ViewControllerc = Maker.addString("ViewController.h");
    Maker.addBucket(getHash(@"ViewController.h"), ViewControllera, ViewControllerb, ViewControllerc);

    auto Bridginga = Maker.addString("TestApp-Bridging-Header.h");
    auto Bridgingb = Maker.addString("/Users/cloud/Documents/iOS/iOS強(qiáng)化班/LoginiOS高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestApp/");
    auto Bridgingc = Maker.addString("TestApp-Bridging-Header.h");
    Maker.addBucket(getHash(@"TestApp-Bridging-Header.h"), Bridginga, Bridgingb, Bridgingc);
    auto ScrollViewa = Maker.addString("TestAppTests-Bridging-Header.h");
    auto ScrollViewb = Maker.addString("/Users/cloud/Documents/iOS/iOS強(qiáng)化班/LoginiOS高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestAppTests/");
    auto ScrollViewc = Maker.addString("TestAppTests-Bridging-Header.h");
    Maker.addBucket(getHash(@"TestAppTests-Bridging-Header.h"), ScrollViewa, ScrollViewb, ScrollViewc);
    
    
    NSData *data = File.getBuffer();
    [data getBytes:&header length:sizeof(struct HMapHeader)];

    [data writeToFile:@"/Users/cloud/Documents/iOS/iOS強(qiáng)化班/LoginiOS高級(jí)強(qiáng)化班/第十六節(jié)課、單元測(cè)試與UI測(cè)試/TestApp/TestApp/mm.hmap" atomically:YES];
    bool is_swapped = false;
    uint32_t (*swap)(uint32_t) = nop_swap;
    if (header.Magic == HMAP_HeaderMagicNumber
        && header.Version == HMAP_HeaderVersion) {
        is_swapped = false;
    } else if (header.Magic == HMAP_SwappedMagic
        && header.Version == HMAP_SwappedVersion) {
        is_swapped = true;
        swap = actually_swap;
    } else {
        warn(/*EX_DATAERR,*/ "header lacks HMAP magic");
        return;
    }
    const uint32_t bucket_count = swap(header.NumBuckets);
    printf(
        "\tHash bucket count: %" PRIu32 "\n"
        "\tString table entry count: %" PRIu32 "\n"
        "\tMax value length: %" PRIu32 " bytes\n",
        bucket_count,
        swap(header.NumEntries),
        swap(header.MaxValueLength));
    const char *raw = (const char *)data.bytes;
    const struct HMapBucket *const buckets = (const struct HMapBucket *const)(raw
            + sizeof(struct HMapHeader));
        const char *const string_table = (raw
            + sizeof(struct HMapHeader)
            + bucket_count*sizeof(struct HMapBucket));
    for (uint32_t i = 0; i < bucket_count; ++i) {
        const struct HMapBucket *const bucket = &(buckets[i]);
        if (swap(bucket->Key) == HMAP_EmptyBucketKey) { continue; }
        // LLVM is careful to sanity-check bucket-count and strlen,
        // but we're just going to assume they're all NUL-terminated
        // and not maliciously crafted input files.
        //
        // This is naive, but this is also not exactly "production" code.
        const char *key = string_table + swap(bucket->Key);
        const char *prefix = string_table + swap(bucket->Prefix);
        const char *suffix = string_table + swap(bucket->Suffix);

        NSLog(@"\t- Bucket %" PRIu32 ": "
            "Key %s -> Prefix %s, Suffix %s\n",
            i,
            key, prefix, suffix);
    }
}
uint32_t
nop_swap(uint32_t u)
{
    return u;
}

uint32_t
actually_swap(uint32_t u)
{
    uint32_t n = (
        ((u & 0xFF) << 24)
        | ((u & 0xFF00) << 8)
        | ((u & 0xFF0000) >> 8)
        | ((u & 0xFF000000) >> 24)
        );
    return n;
}

int
main(int argc, const char *argv[])
{
    assert(actually_swap(HMAP_HeaderMagicNumber) == HMAP_SwappedMagic
        && "failed to properly swap things :(");
       
    write();
    putchar('\n');
    return EXIT_SUCCESS;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。