专栏首页原创分享v8源码解析之hashmap(基于0.1.5)

v8源码解析之hashmap(基于0.1.5)

hashmap是哈希表的实现。

#ifndef V8_HASHMAP_H_
#define V8_HASHMAP_H_

namespace v8 { namespace internal {


// Allocator defines the memory allocator interface
// used by HashMap and implements a default allocator.
class Allocator BASE_EMBEDDED {
 public:
  virtual ~Allocator()  {}
  virtual void* New(size_t size)  { return Malloced::New(size); }
  virtual void Delete(void* p)  { Malloced::Delete(p); }
};


class HashMap {
 public:
  static Allocator DefaultAllocator;

  typedef bool (*MatchFun) (void* key1, void* key2);

  // Dummy constructor.  This constructor doesn't set up the hash
  // map properly so don't use it unless you have good reason.
  HashMap();

  // initial_capacity is the size of the initial hash map;
  // it must be a power of 2 (and thus must not be 0).
  HashMap(MatchFun match,
          Allocator* allocator = &DefaultAllocator,
          uint32_t initial_capacity = 8);

  ~HashMap();

  // HashMap entries are (key, value, hash) tripplets.
  // Some clients may not need to use the value slot
  // (e.g. implementers of sets, where the key is the value).
  struct Entry {
    void* key;
    void* value;
    uint32_t hash;  // the full hash value for key
  };

  // If an entry with matching key is found, Lookup()
  // returns that entry. If no matching entry is found,
  // but insert is set, a new entry is inserted with
  // corresponding key, key hash, and NULL value.
  // Otherwise, NULL is returned.
  Entry* Lookup(void* key, uint32_t hash, bool insert);

  // Empties the hash map (occupancy() == 0).
  void Clear();

  // The number of (non-empty) entries in the table.
  uint32_t occupancy() const  { return occupancy_; }

  // The capacity of the table. The implementation
  // makes sure that occupancy is at most 80% of
  // the table capacity.
  uint32_t capacity() const  { return capacity_; }

  // Iteration
  //
  // for (Entry* p = map.Start(); p != NULL; p = map.Next(p)) {
  //   ...
  // }
  //
  // If entries are inserted during iteration, the effect of
  // calling Next() is undefined.
  Entry* Start() const;
  Entry* Next(Entry* p) const;

 private:
  Allocator* allocator_;
  MatchFun match_;
  Entry* map_;
  // 可分配的元素个数
  uint32_t capacity_;
  // 已分配的元素个数 
  uint32_t occupancy_;
  // 数组末地址
  Entry* map_end() const  { return map_ + capacity_; }
  Entry* Probe(void* key, uint32_t hash);
  void Initialize(uint32_t capacity);
  void Resize();
};


} }  // namespace v8::internal

#endif  // V8_HASHMAP_H_

hashmap.cc

// Copyright 2008 Google Inc. All Rights Reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "v8.h"

#include "hashmap.h"

namespace v8 { namespace internal {

/*
  判断x是不是有且仅有一位是1.如果是则下面的式子成立。
  假设x的第n位是1,x - 1后,n的左边位都是0,右边都是1,n变成0.
  00001000 => 00000111,再和x与,n以及n的右边位是肯定为0的。右边就看
  n的左边的位就可以了。
*/
static inline bool IsPowerOf2(uint32_t x) {
  ASSERT(x != 0);
  return (x & (x - 1)) == 0;
}

// 内存分配器
Allocator HashMap::DefaultAllocator;

// 默认构造函数
HashMap::HashMap() {
  allocator_ = NULL;
  match_ = NULL;
}

// 初始化属性,分配内存
HashMap::HashMap(MatchFun match,
                 Allocator* allocator,
                 uint32_t initial_capacity) {
  allocator_ = allocator;
  match_ = match;
  Initialize(initial_capacity);
}

// 析构函数,释放内存
HashMap::~HashMap() {
  if (allocator_) {
    allocator_->Delete(map_);
  }
}

// 查找或插入一个元素
HashMap::Entry* HashMap::Lookup(void* key, uint32_t hash, bool insert) {
  // Find a matching entry.
  // 找到key和hash对应的索引。
  Entry* p = Probe(key, hash);
  // 找到则返回
  if (p->key != NULL) {
    return p;
  }

  // No entry found; insert one if necessary.
  // 没有找到判断是否需要插入
  if (insert) {
    p->key = key;
    p->value = NULL;
    p->hash = hash;
    // 更新使用的元素个数
    occupancy_++;

    // Grow the map if we reached >= 80% occupancy.
    // 分配的元素过多,重新分配内存,否则导致冲突频繁,影响效率
    if (occupancy_ + occupancy_/4 >= capacity_) {
      Resize();
      // 重新查找对应的元素
      p = Probe(key, hash);
    }

    return p;
  }

  // No entry found and none inserted.
  return NULL;
}

// 
void HashMap::Clear() {
  // Mark all entries as empty.
  // 最后一个元素的末地址
  const Entry* end = map_end();
  // 遍历数组,清空key字段
  for (Entry* p = map_; p < end; p++) {
    p->key = NULL;
  }
  // 分配出去的元素个数为0
  occupancy_ = 0;
}

// 用于迭代
HashMap::Entry* HashMap::Start() const {
  // Next函数的for执行了p++,所以这里要回退一个元素,见Next函数
  return Next(map_ - 1);
}


HashMap::Entry* HashMap::Next(Entry* p) const {
  // 最后一个元素的末地址
  const Entry* end = map_end();
  ASSERT(map_ - 1 <= p && p < end);
  /*
    遍历数组,返回遇到的第一个key非空的节点,
    p++,所以初始化的时候,p指向第一个元素的第一个元素
  */
  for (p++; p < end; p++) {
    if (p->key != NULL) {
      return p;
    }
  }
  return NULL;
}

// 根据key和hash找到哈希表中可用的索引,hash值由调用方提供
HashMap::Entry* HashMap::Probe(void* key, uint32_t hash) {
  ASSERT(key != NULL);

  ASSERT(IsPowerOf2(capacity_));
  // capacity_ - 1防止溢出,实现回环  
  Entry* p = map_ + (hash & (capacity_ - 1));
  // 最后一个元素的末地址
  const Entry* end = map_end();
  ASSERT(map_ <= p && p < end);
  // 至少有一个非NULL,使p->key != NULL成立
  ASSERT(occupancy_ < capacity_);  // guarantees loop termination
  /*
    如果key等于空说明这个项还没被使用,则返回,
    如果key非空,并且hash和key都匹配,则返回。
    hash值不相等或者名字不match,则查找下一个可用的元素,即开放地址法
  */
  while (p->key != NULL && (hash != p->hash || !match_(key, p->key))) {
    p++;
    // 到底了,从头开始
    if (p >= end) {
      p = map_;
    }
  }

  return p;
}

// 申请一个Entry* 数组
void HashMap::Initialize(uint32_t capacity) {
  ASSERT(IsPowerOf2(capacity));
  map_ = reinterpret_cast<Entry*>(allocator_->New(capacity * sizeof(Entry)));
  if (map_ == NULL) V8::FatalProcessOutOfMemory("HashMap::Initialize");
  capacity_ = capacity;
  // 初始化内存数据
  Clear();
}

// 扩展
void HashMap::Resize() {
  // 先保存旧地址的指针
  Entry* map = map_;
  uint32_t n = occupancy_;
  // 重新分配一个更大的数组
  // Allocate larger map.
  Initialize(capacity_ * 2);

  // Rehash all current entries.
  // 重新计算当前哈希表中的元素的位置,n的作用是迁移完n个可用退出循环了,不需要遍历到底
  for (Entry* p = map; n > 0; p++) {
    if (p->key != NULL) {
      // 把旧的元素插入到新的数组中,因为map_更新了,里面是空的,所以会一直插入新的元素到map_
      Lookup(p->key, p->hash, true)->value = p->value;
      n--;
    }
  }
  // 释放旧的地址
  // Delete old map.
  allocator_->Delete(map);
}


} }  // namespace v8::internal

本文分享自微信公众号 - 编程杂技(theanarkh),作者:theanarkh

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-06-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • express框架route.js源码解析

    route.js并不是express里真正的路由代码,他只是其中的一个组成部分,和router(router/index.js)是有区别的。下面先看一下重要的代...

    theanarkh
  • 系统调用之mprotect源码分析(基于linux1.2.13)

    mprotect系统调用是修改内存页属性的,他修改的内容包括vma的内容和页表项内容。linux用vma链表管理一个进程使用的虚拟地址空间。下面是实现代码。

    theanarkh
  • libuv小册之线程池篇

    前言:最近开始写小册子,一篇篇来,写完了再整理总结到一起。循序渐进,重点是优先分析libuv的原理。其他的有时间再写,也希望大家一起。

    theanarkh
  • mapboxGL测量实现

    讲真,MapboxGL里面虽然有测量的功能,但是不太好用,于是就萌生了自己实现的方法。本文几个turf.js来说说mapboxGL中测量的实现。

    lzugis
  • 2019-04-19 程序员都应该搞懂的概念【Mysql核心技术】聊聊事务的实现原理

    https://juejin.im/post/5cb2e3b46fb9a0686e40c5cb?utm_source=gold_browser_extensio...

    Albert陈凯
  • Google V8 引擎

    V8的前世今生 V8是JavaScript渲染引擎,第一个版本随着Chrome的发布而发布(具体时间为2008年9月2日)。在运行JavaScript之前,相比...

    xiangzhihong
  • 使用Java 操作MinIO

    MinIO 是一款高性能、分布式的对象存储系统。它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。MinIO与传...

    JAVA日知录
  • Google V8引擎

    V8的前世今生 V8是JavaScript渲染引擎,第一个版本随着Chrome的发布而发布(具体时间为2008年9月2日)。在运行JavaScript之前,相比...

    xiangzhihong
  • .Net GDI+的图件绘制平台(三)-绘图相关的Utility库

    程序你好
  • mysql递归查询方法|mysql递归查询遇到的坑,教你们解决办法

    相信很多人都用不惯mysql,小编也是,oracle的递归查询很简单。就一句sql就可以搞定,还有不清楚或者突然忘记需要温习的小伙伴们,大家可以看小编发的以前的...

    小小鱼儿小小林

扫码关注云+社区

领取腾讯云代金券