使用C語言模擬Node(一) —— 數據結構

項目簡介

基於c語言實現的仿node庫,主要實現js的事件循環機制(單線程異步)和幾大主要模塊(event、socket、fs等)。這只是一個練手的項目,不能保證代碼的絕對可靠性,如果發現bug,歡迎提issue,項目地址:https://gitee.com/lyxfj/async

數據結構

  1. JavaScript常用的數據類型,先來看下和C語言數據類型的差異和擬定的替代方案:
js c語言 替代方案
number int、double 直接使用
string char * 使用宏
boolean enum 使用枚舉類型定義
object struct 定義結構體,或者新建模塊HashMap
array [] 新建模塊Array
function 函數 函數指針
  1. 基本結構的實現
  • Number類型
    JavaScript中number類型包括了整型和浮點型兩種,C語言中可以直接使用int和double。

    *注: float類型精度不夠,所以只採用了double

  • Boolean類型
    枚舉類型解決:

    enum Boolean { True = 1, False = 0 };
    
  • 字符串類型
    宏定義如下:

    #define string char *
    #define newString(string) strcpy((char *)malloc(strlen(string) + 1), string)
    
  • object類型
    JavaScript中object類型有很多種情況,nullundefined直接使用全局宏NULL即可,多數情況寫可以用關鍵字struct定義一個結構體解決,但是當添加屬性和刪除屬性時就會很不方便,所以先實現一個HashMap結構,首先定義鍵值對結構體Entry,爲了解決哈希衝突添加了一個指針,將衝突值以鏈表的形式掛在後面:

    typedef struct entry {
        void * key;             // 鍵
        void * value;           // 值
        struct entry * next;    // 衝突鏈表
    }*Entry;
    
    #define newEntry() NEW(struct entry)
    #define newEntryList(length) (Entry)malloc(length * sizeof(struct entry))
    

    接着定義HashMap結構體,實現思路很簡單,建立一個Entry數組作爲存儲空間,然後根據傳入的key計算出一個哈希地址,當做數組的索引存入,讀取的時候通過計算出的索引可以在數組中直接取出值,時間複雜度爲O(1)size是當前存儲鍵值對的數量,而listSize是當前數組的大小,數組的每一項其實都是鏈表的頭節點,這就有可能導致size大於listSize,當size大於listSize的時候一定發生了衝突,所以在調用存儲方法hashMap->put的時候會判斷size是否超過了listSize,如果超過了會擴充空間,減少衝突,加快索引速度。

    // 哈希結構
    typedef struct hashMap *HashMap;
    
    #define newHashMap() NEW(struct hashMap)
    
    // 哈希函數類型
    typedef int(*HashCode)(HashMap, void * key);
    
    // 判等函數類型
    typedef Boolean(*Equal)(void * key1, void * key2);
    
    // 添加鍵函數類型
    typedef void(*Put)(HashMap hashMap, void * key, void * value);
    
    // 獲取鍵對應值的函數類型
    typedef void * (*Get)(HashMap hashMap, void * key);
    
    // 刪除鍵的函數類型
    typedef Boolean(*Remove)(HashMap hashMap, void * key);
    
    // 清空Map的函數類型
    typedef void(*Clear)(HashMap hashMap);
    
    // 判斷鍵值是否存在的函數類型
    typedef Boolean(*Exists)(HashMap hashMap, void * key);
    
    typedef struct hashMap {
        int size;           // 當前大小
        int listSize;       // 有效空間大小
        HashCode hashCode;  // 哈希函數
        Equal equal;        // 判等函數
        Entry list;         // 存儲區域
        Put put;            // 添加鍵的函數
        Get get;            // 獲取鍵對應值的函數
        Remove remove;      // 刪除鍵
        Clear clear;        // 清空Map
        Exists exists;      // 判斷鍵是否存在
    }*HashMap;
    
    // 默認哈希函數
    static int defaultHashCode(HashMap hashMap, void * key);
    
    // 默認判斷鍵值是否相等
    static Boolean defaultEqual(void * key1, void * key2);
    
    // 默認添加鍵值對
    static void defaultPut(HashMap hashMap, void * key, void * value);
    
    // 默認獲取鍵對應值
    static void * defaultGet(HashMap hashMap, void * key);
    
    // 默認刪除鍵
    static Boolean defaultRemove(HashMap hashMap, void * key);
    
    // 默認判斷鍵是否存在
    static Boolean defaultExists(HashMap hashMap, void * key);
    
    // 默認清空Map
    static void defaultClear(HashMap hashMap);
    
    // 創建一個哈希結構
    HashMap createHashMap(HashCode hashCode, Equal equal);
    

    這裏的一些方法定義成了static,爲的是在文件外不可訪問,只能通過實例化的HashMap的屬性去調用他們,這樣用戶可以隨時更換其中的模塊而不影響整體功能。

    還要給HashMap實現一個iterator接口,這是後續實現對象屬性遍歷的基礎:

    // 創建一個哈希結構
    HashMap createHashMap(HashCode hashCode, Equal equal);
    
    // 創建哈希結構迭代器
    Iterator createIterator(HashMap hashMap);
    
    // 迭代器是否有下一個
    Boolean hasNextIterator(Iterator iterator);
    
    // 迭代到下一次
    Iterator nextIterator(Iterator iterator);
    
    // 釋放迭代器內存
    void freeIterator(Iterator iterator);
    

    完整的實現如下:

    #include"hashMap.h"
    
    int defaultHashCode(HashMap hashMap, void * key)
    {
        char * k = (char *)key;
        unsigned long h = 0;
        while (*k) {
            h = (h << 4) + *k++;
            unsigned long g = h & 0xF0000000L;
            if (g) {
                h ^= g >> 24;
            }
            h &= ~g;
        }
        return h % hashMap->listSize;
    }
    
    Boolean defaultEqual(void * key1, void * key2)
    {
        return strcmp((char *)key1, (char *)key2) ? False : True;
    }
    
    void defaultPut(HashMap hashMap, void * key, void * value)
    {
    
        int index = hashMap->hashCode(hashMap, key);
        if (hashMap->list[index].key == NULL) {
            hashMap->size++;
            // 該地址爲空時直接存儲
            hashMap->list[index].key = key;
            hashMap->list[index].value = value;
        }
        else {
                
            Entry current = &hashMap->list[index];
            while (current!= NULL) {
                if (hashMap->equal(key, current->key)) {
                    // 對於鍵值已經存在的直接覆蓋
                    hashMap->list[index].value = value;
                    return;
                }
                current = current->next;
            };
    
            // 發生衝突則創建節點掛到相應位置的next上
            Entry entry = newEntry();
            entry->key = key;
            entry->value = value;
            entry->next = hashMap->list[index].next;
            hashMap->list[index].next = entry;
            hashMap->size++;
        }
    
        if (hashMap->size > hashMap->listSize) {
            Entry tempList = newEntryList(hashMap->size);
            Iterator iterator = createIterator(hashMap);
            int length = hashMap->size;
            for (int index = 0; hasNextIterator(iterator); index++) {
                // 迭代取出所有鍵值對
                iterator = nextIterator(iterator);
                tempList[index].key = iterator->entry->key;
                tempList[index].value = iterator->entry->value;
                tempList[index].next = NULL;
            }
            freeIterator(iterator);
    
            // 清除原有鍵值對數據
            hashMap->size = 0;
            for (int i = 0; i < hashMap->listSize; i++) {
                Entry current = &hashMap->list[i];
                current->key = NULL;
                current->value = NULL;
                if (current->next != NULL) {
                    while (current->next != NULL) {
                        Entry temp = current->next->next;
                        free(current->next);
                        current->next = temp;
                    }
                }
            }
    
            // 內存擴充至原來的兩倍
            // *注: 擴充時考慮的是當前存儲元素數量與存儲空間的大小關係,而不是存儲空間是否已經存滿,
            // 例如: 存儲空間爲10,存入了10個鍵值對,但是全部衝突了,所以存儲空間空着9個,其餘的全部掛在一個上面,
            // 這樣檢索的時候和遍歷查詢沒有什麼區別了,可以簡單這樣理解,當我存入第11個鍵值對的時候一定會發生衝突,
            // 這是由哈希函數本身的特性(取模)決定的,衝突就會導致檢索變慢,所以這時候擴充存儲空間,對原有鍵值對進行
            // 再次散列,會把衝突的數據再次分散開,加快索引定位速度。
            hashMap->listSize *= 2;
            Entry relist = (Entry)realloc(hashMap->list, hashMap->listSize * sizeof(struct entry));
            if (relist != NULL) {
                hashMap->list = relist;
                relist = NULL;
            }
    
            // 初始化數據
            for (int i = 0; i < hashMap->listSize; i++) {
                hashMap->list[i].key = NULL;
                hashMap->list[i].value = NULL;
                hashMap->list[i].next = NULL;
            }
    
            // 將所有鍵值對重新寫入內存
            for (int i = 0; i < length; i++) {
                hashMap->put(hashMap, tempList[i].key, tempList[i].value);
            }
            free(tempList);
            // 將新鍵值對寫入
            hashMap->put(hashMap, key, value);
        }
    }
    
    void * defaultGet(HashMap hashMap, void * key)
    {
        int index = hashMap->hashCode(hashMap, key);
        Entry entry = &hashMap->list[index];
        while (entry->key != NULL && !hashMap->equal(entry->key, key)) {
            entry = entry->next;
        }
        return entry->value;
    }
    
    Boolean defaultRemove(HashMap hashMap, void * key)
    {
        int index = hashMap->hashCode(hashMap, key);
        Entry entry = &hashMap->list[index];
        if (entry->key == NULL) {
            return False;
        }
        if (hashMap->equal(entry->key, key)) {
            hashMap->size--;
            if (entry->next != NULL) {
                entry->key = entry->next->key;
                entry->value = entry->next->value;
                entry->next = entry->next->next;
                free(entry->next);
            }
            else {
                entry->key = entry->value = NULL;
            }
            return True;
        }
        else {
            Entry p = entry;
            entry = entry->next;
            while (entry != NULL) {
                if (hashMap->equal(entry->key, key)) {
                    hashMap->size--;
                    p->next = entry->next;
                    free(entry);
                    return True;
                }
                p = entry;
                entry = entry->next;
            };
            return False;
        }
    
        if (hashMap->exists(hashMap, key)) {
        }
        else {
            return False;
        }
    }
    
    Boolean defaultExists(HashMap hashMap, void * key)
    {
        int index = hashMap->hashCode(hashMap, key);
        Entry entry = &hashMap->list[index];
        if (entry->key == NULL) {
            return False;
        }
        if (hashMap->equal(entry->key, key)) {
            return True;
        }
        if (entry->next != NULL) {
            do {
                if (hashMap->equal(entry->key, key)) {
                    return True;
                }
                entry = entry->next;
    
            } while (entry != NULL);
            return False;
        }
        else {
            return False;
        }
    }
    
    void defaultClear(HashMap hashMap)
    {
        for (int i = 0; i < hashMap->listSize; i++) {
            // 釋放衝突值內存
            Entry entry = hashMap->list[i].next;
            while (entry != NULL) {
                Entry next = entry->next;
                free(entry);
                entry = next;
            }
            hashMap->list[i].next = NULL;
        }
        // 釋放存儲空間
        free(hashMap->list);
        hashMap->list = NULL;
        hashMap->size = -1;
        hashMap->listSize = 0;
    }
    
    HashMap createHashMap(HashCode hashCode, Equal equal)
    {
        HashMap hashMap = newHashMap();
        hashMap->size = 0;
        hashMap->listSize = 10;
        hashMap->hashCode = hashCode == NULL ? defaultHashCode : hashCode;
        hashMap->equal = equal == NULL ? defaultEqual : equal;
        hashMap->exists = defaultExists;
        hashMap->get = defaultGet;
        hashMap->put = defaultPut;
        hashMap->remove = defaultRemove;
        hashMap->clear = defaultClear;
        
        // 起始分配10個內存空間,溢出時會自動擴充
        hashMap->list = newEntryList(hashMap->listSize);
        Entry p = hashMap->list;
        for (int i = 0; i < hashMap->listSize; i++) {
            p[i].key = p[i].value = p[i].next = NULL;
        }
        return hashMap;
    }
    
    Iterator createIterator(HashMap hashMap)
    {
        Iterator iterator = newIterator();
        iterator->hashMap = hashMap;
        iterator->count = 0;
        iterator->hashCode = -1;
        iterator->entry = NULL;
        return iterator;
    }
    
    Boolean hasNextIterator(Iterator iterator)
    {
        return iterator->count < iterator->hashMap->size ? True : False;
    }
    
    Iterator nextIterator(Iterator iterator)
    {
        if (hasNextIterator(iterator)) {
            if (iterator->entry != NULL && iterator->entry->next != NULL) {
                iterator->count++;
                iterator->entry = iterator->entry->next;
                return iterator;
            }
            while (++iterator->hashCode < iterator->hashMap->listSize) {
                Entry entry = &iterator->hashMap->list[iterator->hashCode];
                if (entry->key != NULL) {
                    iterator->count++;
                    iterator->entry = entry;
                    break;
                }
            }
        }
        return iterator;
    }
    
    void freeIterator(Iterator iterator)
    {
        free(iterator);
    }
    
  • Array結構類型
    js的數組長度可以自由變化,而C語言中數組的長度是固定的,變通點可以動態分配內存或者鏈表,但是在某些情況下,動態分配內存不僅浪費存儲空間,頻繁擴充內存性能也不是很好,鏈表就沒有辦法隨機存取。配合js數組的幾個常用方法實現詳細的對比分析如下:

    對比項 動態內存分配 雙向鏈表
    隨機存取 支持 不支持,添加方法indexOf
    插入、刪除 要移動大量元素 只需要改變指針指向
    存儲密度 很高 較低
    sort 無影響 無影響
    pop 很快 很快
    push 很快 很快
    reverse 較慢 很快
    shift 移動大量元素,較慢 很快
    slice 很快 需要逐個定位,較慢
    filter 一致 一致
    concat 一致 一致

    綜合考慮,採用雙向鏈表的方式實現。

    定義雙向鏈表的結構:

    // 雙向鏈表
    typedef struct list {
        let item;
        struct list * next;
        struct list * last;
    }*ListNode, *List;
    
    #define newList() NEW(struct list)
    #define newListNode() NEW(struct list)
    

    定義Array結構:

    typedef struct array *Array;
    
    typedef Array(*Concat)(Array a, Array b);
    
    typedef let (*Pop)(Array array);
    
    typedef int(*Push)(Array array, let item);
    
    typedef void(*Reverse)(Array array);
    
    typedef let (*Shift)(Array array);
    
    typedef int (*Unshift)(Array array, let item);
    
    typedef Array(*Slice)(Array array, int start, int end);
    
    typedef int(*SortBy)(let item1, let item2);
    
    typedef void(*Sort)(Array array, SortBy sort);
    
    typedef Boolean(*FilterBy)(let item);
    
    typedef Array(*Filter)(Array array, FilterBy filter);
    
    typedef let (*IndexOf)(Array array, int index);
    
    typedef struct array {
        Pop pop;
        List head;
        List tail;
        Sort sort;
        Push push;
        int length;
        Shift shift;
        Slice slice;
        Filter filter;
        Concat concat;
        Reverse reverse;
        Unshift unshift;
        IndexOf indexOf;
    }*Array;
    
    #define newArray() NEW(struct array)
    

    Array對象方法:

    // 連接兩個數組,返回一個新的數組
    static Array concat(Array a, Array b);
    
    // 返回數組最後一個元素
    static let pop(Array a);
    
    // 在數組最後插入一個元素,返回新長度
    static int push(Array array, let item);
    
    // 反轉整個數組,會修改原數組
    static void reverse(Array array);
    
    // 刪除並返回數組的第一個元素
    static let shift(Array array);
    
    // 刪除並返回數組的第一個元素
    static int unshift(Array array, let item);
    
    // 從數組中選取一部分,構成一個新數組返回
    static Array slice(Array array, int start, int end);
    
    // 對數組的元素進行排序
    static void sort(Array array, SortBy sort);
    
    // 過濾出一部分元素,filter返回True時保留,構成新數組返回
    static Array filter(Array array, FilterBy filter);
    
    // 返回指定索引位置的值
    static let indexOf(Array array, int index);
    
    // 創建數組
    Array createArray(int size);
    
    // 釋放數組
    void freeArray(Array * array);
    

    *注: 區別js的slice,它的分片範圍是[start, end),當第二個參數不設置的時候默認取到最後一位,而C語言參數不能空白,後續會使用va_list動態參數來解決。

    爲Array創建迭代器:

    // 迭代器結構
    typedef struct arrayIterator {
        ListNode node;	// 迭代器當前指向
        int count;		// 迭代次數
        Array array;
    }*ArrayIterator;
    
    #define newArrayIterator() NEW(struct arrayIterator)
    
    // 創建數組迭代器
    ArrayIterator createArrayIterator(Array array);
    
    // 迭代器是否有下一個
    Boolean hasNextArrayIterator(ArrayIterator iterator);
    
    // 迭代到下一次
    ArrayIterator nextArrayIterator(ArrayIterator iterator);
    
    // 釋放迭代器內存
    void freeArrayIterator(ArrayIterator * iterator);
    

    完整實現如下:

    #include "array.h"
    
    Array concat(Array a, Array b)
    {
        Array array = createArray(0);
        ListNode node = a->head;
        a->tail->next = b->head;
        while (node != NULL) {
            array->push(array, node->item);
            node = node->next;
        }
        a->tail->next = NULL;
        return array;
    }
    
    let pop(Array array)
    {
        let item = NULL;
        if (array->length > 0) {
            array->length --;
            item = array->tail->item;
            ListNode node = array->tail;
            if (array->length == 0) {
                array->head = array->tail = NULL;
            }
            else {
                array->tail = array->tail->last;
                array->tail->next = NULL;
            }
            free(node);
        }
        return item;
    }
    
    int push(Array array, let item)
    {
        array->length++;
        ListNode node = newListNode();
        node->item = item;
        node->next = NULL;
        if (array->tail == NULL) {
            node->last = NULL;
            array->tail = node;
            array->head = node;
        }
        else {
            array->tail->next = node;
            node->last = array->tail;
            array->tail = node;
        }
        return 0;
    }
    
    void reverse(Array array)
    {
        ListNode node = array->head;
        array->head = array->tail;
        array->tail = node;
        while (node != NULL) {
            ListNode temp = node->next;
            node->next = node->last;
            node->last = temp;
            node = node->last;
        }
    }
    
    let shift(Array array)
    {
        let item = NULL;
        if (array->length > 0) {
            ListNode node = array->head;
            array->length--;
            if (array->head->next != NULL) {
                array->head = array->head->next;
                array->head->last = NULL;
            }
            else {
                array->head = array->tail = NULL;
            }
            node->last = node->next = NULL;
            item = node->item;
            free(node);
        }
        return item;
    }
    
    int unshift(Array array, let item)
    {
        array->length++;
        ListNode node = newListNode();
        node->item = item;
        node->next = array->head;
        node->last = NULL;
        array->head->last = node;
        array->head = node;
        return array->length;
    }
    
    // 區別js的slice,js的選取區間爲[start, end),此處爲[start, end]
    Array slice(Array array, int start, int end)
    {
        Array sliceArray = createArray(0);
    
        // 驗證區間合法性
        if (
            (start > array->length && end < -array->length)
            || ((start^end) > 0 && start >= end)
            || (start >= 0 && end < 0 && start >= end + array->length)
            || (start < 0 && end >= 0 && start + array->length >= end)
        ) return sliceArray;
    
        ListNode startNode, endNode;
        start = start >= 0 ? start : start + array->length;
        start = RANGE(0, array->length - 1, start);
        end = end >= 0 ? end : end + array->length;
        end = RANGE(0, array->length - 1, end);
    
        int half = array->length >> 1;
        if (start < half) {
            startNode = array->head;
            while (start-- != 0) {
                startNode = startNode->next;
            }
        }
        else {
            startNode = array->tail;
            while (start-- != 0) {
                startNode = startNode->last;
            }
        }
    
        if (end < half) {
            endNode = array->head;
            while (end-- != 0) {
                endNode = endNode->next;
            }
        }
        else {
            endNode = array->tail;
            while (end-- != 0) {
                endNode = endNode->last;
            }
        }
    
        while (startNode != endNode) {
            sliceArray->push(sliceArray, startNode->item);
            startNode = startNode->next;
        }
        sliceArray->push(sliceArray, endNode->item);
        return sliceArray;
    }
    
    void sort(Array array, SortBy sort)
    {
        if (array->length < 2) return;
    
        typedef struct stackItem {
            ListNode i;
            ListNode j;
            ListNode k;
            void * value;
        }*StackItem;
    
        Array stack = createArray(0);
    
        StackItem first = NEW(struct stackItem);
        first->i = array->head;
        first->j = array->tail;
        first->k = array->head;
        first->value = array->head->item;
    
        stack->push(stack, first);
    
        while (stack->length != 0) {
            StackItem stackItem = (StackItem)stack->pop(stack);
            ListNode i = stackItem->i;
            ListNode j = stackItem->j;
            void * k = stackItem->value;
    
            while (i != j) {
                while (sort(j->item, k) > 0 && i != j) {
                    if (j->last == NULL) break;
                    j = j->last;
                }
                i->item = j->item;
    
                while (sort(i->item, k) <= 0 && i != j) {
                    if (i->next == NULL) break;
                    i = i->next;
                }
                j->item = i->item;
            }
            i->item = k;
    
            if (i != stackItem->i && i->last != stackItem->i) {
                StackItem left = NEW(struct stackItem);
                left->i = stackItem->i;
                left->j = i->last;
                left->k = stackItem->i;
                left->value = left->k->item;
    
                stack->push(stack, left);
            }
            
            if (i != stackItem->j && i->next != stackItem->j) {
                StackItem right = NEW(struct stackItem);
                right->i = i->next;
                right->j = stackItem->j;
                right->k = i->next;
                right->value = right->k->item;
    
                stack->push(stack, right);
            }
            
            free(stackItem);
        }
    }
    
    Array filter(Array array, FilterBy filter)
    {
        Array result = createArray(0);
        ListNode node = array->head;
        while (node != NULL) {
            if (filter(node->item)) {
                result->push(result, node->item);
            }
            node = node->next;
        }
        return result;
    }
    
    let indexOf(Array array, int index)
    {
        // 索引位置與實際位置差1
        index ++ ;
        if (index > 0 && index <= array->length) {
            ListNode node;
            if (index <= array->length / 2) {
                node = array->head;
                while (index-- > 1) {
                    node = node->next;
                }
            }
            else {
                node = array->tail;
                index = array->length - index;
                while (index-- > 0) {
                    node = node->last;
                }
            }
            return node->item;
        }
        return NULL;
    }
    
    Array createArray(int size)
    {
        Array array = newArray();
        array->pop = pop;
        array->sort = sort;
        array->push = push;
        array->shift = shift;
        array->slice = slice;
        array->concat = concat;
        array->filter = filter;
        array->reverse = reverse;
        array->unshift = unshift;
        array->indexOf = indexOf;
        array->head = array->tail = NULL;
        if (size > 0) {
            while (size-- > 0) {
                array->push(array, NULL);
            }
        }
        else {
            array->length = 0;
        }
        
        return array;
    }
    
    void freeArray(Array * array)
    {
        Array temp = *array;
        while (temp->length != 0) {
            temp->pop(temp);
        }
        *array = NULL;
    }
    
    ArrayIterator createArrayIterator(Array array)
    {
        ArrayIterator iterator = newArrayIterator();
        iterator->count = 0;
        iterator->node = NULL;
        iterator->array = array;
        return iterator;
    }
    
    Boolean hasNextArrayIterator(ArrayIterator iterator)
    {
        return iterator->count < iterator->array->length ? True : False;
    }
    
    ArrayIterator nextArrayIterator(ArrayIterator iterator)
    {
        if (hasNextArrayIterator(iterator)) {
    
            iterator->node = iterator->node == NULL ? iterator->array->head : iterator->node->next;
            iterator->count++;
        }
        return iterator;
    }
    
    void freeArrayIterator(ArrayIterator * iterator)
    {
        free(*iterator);
        *iterator = NULL;
    }
    

    後續會使用動態內存實現另一個版本,使用者可以根據實際場景自由選擇。

下一篇: 使用C語言模擬Node(二) —— 事件循環

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章