iOS-底层原理40-Hmap使用(美团文章分析)

《iOS底层原理文章汇总》

1.OC&Swift测试工程配置

I.在测试工程中测试主工程中代码,直接引入头文件进行调用

image

II.编写Swift测试用例

创建时点击Create Bridging Header,此时依然无法使用ViewController类中的方法

image
image.png

在主工程中创建swift工程,让系统生成桥接文件,在桥接文件中引入“ViewController.h”,此时还需在需要使用OC文件的地方引入主Module才能使用ViewController

image
image
image

Swift测试文件使用OC代码的时候
A.主工程Bridge文件导出相关的头文件
B.Swift测试文件引入主工程Module

TestAppTests-Bridging-Header.h是将测试工程中的OC文件给测试工程中的swift使用

TestApp-Bridging-Header.h是将主工程中的OC文件给其他swift代码使用

在主工程swift文件中新建两个module,在测试工程中如何使用呢?

image

swift没有权限控制符的时候,默认为不开放,此时用@testable import导入主工程公开权限,只是针对测试工程使用

image

若swift类使用public、open修饰,则import TestApp能访问到

image

若没有修饰或internal修饰,则需要使用@testable import TestApp

image.png

若在OC中使用swift model,并不能直接使用,需要引入系统帮我们默认生成的头文件,头文件以当前App名称开头-Swift.h

image

同理,若要在测试工程中使用swift相关的代码,需要引入TestApp-Swift.h头文件,然而TestApp和TestAppTests属于两个target,两个不同的项目,TestApp系统默认生成的TestApp-Swift.h,我们是直接访问不到的,需要在header search path中指明路径

image

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

image

Xcode会自动生成一个环境变量$CONFIGURATION_TEMP_DIR,
指向如下目录


image

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

image

递归和不递归的区别是,递归情况下,会在给的
路径下面的子文件夹中去寻找,若非递归状态下
不会在子目录下面去找,相当于需要给绝对路径

image

递归状态下,配置绝对路径

image.png

2.header search path底层是-I,接收两个参数,头文件的目录,指定hmap文件

hmap:key-value,为什么-I可以通过hmap解析到头文件所在的目录,这是美团这篇文章的重点一款可以让大型iOS工程编译速度提升50%的工具

如何通过hmap加快编译速度?

hmap是一个容器,包裹着头文件所在的目录,通过xcode去查找文件的时候更快速

hmap文件如何生成?底层结构是什么样的?

查看.m文件的编译,发现在编译期底层通过-I参数链接了-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

来到这个目录,发现有很多hmap文件

image

通过cat查看.hmap中的内容,怎么理解.hmap底层到底是什么样的数据结构体呢?借助llvm工程查看.hmap的结构,hmap文件由HampHeader和HmapBucket,若target中包含3个头文件,则会有3个HmapBucket,Bucket的数量与头文件的数量相等,
通过读取Hmapheader,知道当前二进制里面有多少个Bucket,Bucket表示当前的hmap里面保存了多少个头文件的路径,通过Bucket,底下保存了字符串,Bucket中有偏移量,可以根据偏移量计算出保存字符串的位置,从而读取出头文件的路径

前半部分是配置文件,后半部分存储真正的信息

image.png

怎么将文件读取成结构体,再读取出头文件的字符串呢?
在llvm中的LexTests target中我们发现了结构体HMapBucket和HMapHeader,同时发现将.hmap解析成HMapBucket和HMapHeader的源代码

image.png
image

通过C函数读取hmap文件,提取其中的HmapHeader和HmapBucket,将二进制转化为结构体,通过打印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函数中,通过C语言和结构体内存平移原则,先读取HmapHeader,再读取HmapBucket
HMapHeader -> 指明了有多少个Bucket
每一个Bucket代表了一个头文件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;
    // 二进制数据
    // 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 数据对齐
    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 -》指明了有多少个load commands
    // HMapHeader -》指明了有多少个Bucket
    // 每一个Bucket代表了一个头文件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课程/第十六节课、单元测试与UI测试/上课代码/01-OC&Swift测试工程配置/LGTestApp/LGTestApp/
 //头文件的后半部分
 Suffix LGTestApp-Bridging-Header.h
 // hmap 对应 按照规则存储一堆的头文件
 */

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) {
        //导出 header map
        dump(argv[i]);
        putchar('\n');
    }
    return EXIT_SUCCESS;
}

读取出来头文件结果,包含工程中的5个头文件,每个bucket由key、prefix、suffix组成,分别为工程中的TestApp-Bridging-Header.h,ViewController.h,AppDelegate.h,SceneDelegate.h,TestAppTests-Bridging-Header.h,并不说一定是8个bucket,存在数据对齐规则
key:表示主题
Prefix:表示头文件的前半部分
Suffix:表示头文件的后半部分
hmap对应按照规则存储的一堆头文件

Header map: /Users/cloud/Documents/iOS/iOS强化班/LoginiOS高级强化班/第十六节课、单元测试与UI测试/上课代码/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强化班/Login iOS 高级强化班/第十六节课、单元测试与UI测试/TestApp/TestApp/, Suffix TestApp-Bridging-Header.h
    - Bucket 1: Key ViewController.h -> Prefix /Users/cloud/Documents/iOS/iOS强化班/Login iOS 高级强化班/第十六节课、单元测试与UI测试/TestApp/TestApp/, Suffix ViewController.h
    - Bucket 2: Key AppDelegate.h -> Prefix /Users/cloud/Documents/iOS/iOS强化班/Login iOS 高级强化班/第十六节课、单元测试与UI测试/TestApp/TestApp/, Suffix AppDelegate.h
    - Bucket 3: Key SceneDelegate.h -> Prefix /Users/cloud/Documents/iOS/iOS强化班/Login iOS 高级强化班/第十六节课、单元测试与UI测试/TestApp/TestApp/, Suffix SceneDelegate.h
    - Bucket 7: Key TestAppTests-Bridging-Header.h -> Prefix /Users/cloud/Documents/iOS/iOS强化班/Login iOS 高级强化班/第十六节课、单元测试与UI测试/TestApp/TestAppTests/, Suffix TestAppTests-Bridging-Header.h
image

3.美团的文章说生成的头文件为n,生成的hmap文件为n+1,1代表1个,n代表n个

-I链接头文件可以接受两种参数,第一种是接收一个路径,第二种是接收.hmap文件

两种查找头文件的方式

I.查找头文件通过目录的方式查找,如cocoapods自动生成的debug.xcconfig文件

缺点:若在多个目录里面寻找一个头文件,会比较耗时,如果能把一个文件路径提前放在一个hmap文件里面,去读取文件里面的路径会快很多,header search paths中有头文件的数量限制的

若头文件比较少,不需要用到.hmap文件,头文件越少,.hmap的作用越不明显,
头文件数量越多越明显

image.png

II.通过加载.hmap文件解析方式查找

4.通过终端直接查看.hmap文件里面的内容

A.将DumpHeaderMap的可执行文件复制到全局目录路径中,往shell配置文件中导出路径,将可执行文件路径放入~/.bashrc中,执行source ~/.bashrc,使环境变量生效

image.png
image
image

B.检验DumpHeaderMap命令是否生效,发现已经生效,此时头文件的路径均为绝对路径,在什么情况下头文件的路径会是相对路径呢?

image
image

在前文的工程中新建Headers Phase,将ViewController拖到Public目录下,Cmd + B编译后,得到新的.hmap文件,查看路径,发现ViewController.h由绝对路径变为了相对路径

image.png
image.png
image

将ViewController.h拖入Project目录下,此时编译生成的.hmap文件里面的ViewController.h文件才是绝对路径目录

image
image

image

美团文章里面,n+1中n表示私有头文件(绝对路径的可以放到一起),1表示公共头文件(公有的头文件放到一起)

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

前文在测试工程中,使用swift相关代码,需要引入TestApp-Swift.h头文件,而测试工程和主工程属于不同的target,要引入头文件,需要在header search path中指明路径,此时可以模拟生成一个.hmap文件,包含TestApp-Swift.h头文件来替换header search path中的路径

I.生成包含TestApp-Swift.h头文件的.hmap文件,将TestApp-Swift.h头文件拷贝到工程SRCROOT目录下

image

II.在配置工程中去插入Bucket,只配置一个TestApp-Swift.h,生成.mm文件中包含这个Bucket

image
image
image.png

III.在TestApp的测试工程TestAppTests的header search paths中指定包含TestApp-Swift.h文件的mm.hmap文件的路径,此时发现仍旧找不到TestApp-Swift.h头文件的路径,原因是.hmap中包含的.h文件要全,当前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

重新运行Cmd + U 进行编译,看工程是否能找到TestApp-Swift.h头文件
此时仍然找不到TestApp-Swift.h头文件

image

查看当前项目TestApp中Xcode生成的TestApp-project-headers.hmap文件中包含的头文件,发现有5个头文件,继续往.mm文件中添加TestAppTests-Bridging-Header.h头文件

image

增加TestAppTests-Bridging-Header.h头文件后,mm.hmap中的头文件的数量变为4个,此时编译还是不通过,加大数字typedef MapFile<8, 750> FileTy;重新编译

image
image

typedef MapFile<8, 750> FileTy,控制生成多少个NumBuckets,NumBytes

image

此时能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 :几个Bucket
    // 150: buffer大小
    // 生成的NumBuckets NumBytes
    // NumBuckets NumBytes有影响的
    // 研究
    // 脚本 -》 Cocoapods pub 实际是脚本表现形式之一
    // 最大的亮亮点 不是跑通 害怕
//    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生成的
    // 原理 -》想象原理没有错
    // iOS为什么难
    // Apple
    // hmap -》搜 -〉搜不出来啥
    auto a = Maker.addString("TestApp-Swift.h");
    // 前半部分
    auto b = Maker.addString("/Users/cloud/Documents/iOS/iOS强化班/LoginiOS高级强化班/第十六节课、单元测试与UI测试/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强化班/LoginiOS高级强化班/第十六节课、单元测试与UI测试/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强化班/LoginiOS高级强化班/第十六节课、单元测试与UI测试/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强化班/LoginiOS高级强化班/第十六节课、单元测试与UI测试/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强化班/LoginiOS高级强化班/第十六节课、单元测试与UI测试/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;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335