Node 中 V8 C++ class 到 JS obj

参照 nodejs.org 文档,用 C++ 写 Node 的原生模块。

下面使用 C++ 为 JS 写一个简单的原生 Jenkins Hash 算法。

Hash.h

/**
 * Hash.h
 *
 * @author YanWen <i@yanwen.email>
 * @modified 2018-05-08
 */
#ifndef HASH_H
#define HASH_H

#include <node.h>
#include <node_object_wrap.h>

// #include <iostream>
#include <string>

namespace hash {

class Hash : public node::ObjectWrap {
 public:
  static void Init(v8::Local<v8::Object> exports);

 private:
  explicit Hash(std::string value = "yanwen");
  virtual ~Hash();

  static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void Jenkins(const v8::FunctionCallbackInfo<v8::Value>& args);
  static v8::Persistent<v8::Function> constructor;
  std::string value_;
};

}  // namespace hash

#endif

Hash.cc

/**
 * Hash.cc
 *
 * @author YanWen <i@yanwen.email>
 * @modified 2018-05-08
 */
#include "Hash.h"

namespace hash {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Value;

// using std::cout;
using std::string;

Persistent<Function> Hash::constructor;

Hash::Hash(string value) : value_(value) {
}

Hash::~Hash() {
}

void Hash::Init(Local<Object> exports) {
  Isolate* isolate = exports->GetIsolate();

  // Prepare constructor template
  Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
  tpl->SetClassName(String::NewFromUtf8(isolate, "Hash"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  // Prototype
  NODE_SET_PROTOTYPE_METHOD(tpl, "jenkins", Jenkins);

  constructor.Reset(isolate, tpl->GetFunction());
  exports->Set(String::NewFromUtf8(isolate, "Hash"),
               tpl->GetFunction());
}

void Hash::New(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  if (args.IsConstructCall()) {
    // Invoked as constructor: `new Hash(...)`
    string value = args[0]->IsUndefined() ? "yanwen" : string( * String::Utf8Value(isolate, args[0]->ToString()) );
    Hash* obj = new Hash(value);
    obj->Wrap(args.This());
    args.GetReturnValue().Set(args.This());
  } else {
    // Invoked as plain function `Hash(...)`, turn into construct call.
    const int argc = 1;
    Local<Value> argv[argc] = { args[0] };
    Local<Context> context = isolate->GetCurrentContext();
    Local<Function> cons = Local<Function>::New(isolate, constructor);
    Local<Object> result =
        cons->NewInstance(context, argc, argv).ToLocalChecked();
    args.GetReturnValue().Set(result);
  }
}

void Hash::Jenkins(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  Hash* obj = ObjectWrap::Unwrap<Hash>(args.Holder());

  uint32_t hash = 0;
  for (size_t i = 0; i < obj->value_.length(); i++) {
    hash += obj->value_[i];
    hash += hash << 10;
    hash ^= hash >> 6;
  }

  hash += hash << 3;
  hash ^= hash >> 11;
  hash += hash << 15;

  args.GetReturnValue().Set(Number::New(isolate, hash));
}

}  // namespace hash

yw_hash.cc

/**
 * yw_hash.cc
 *
 * @author YanWen <i@yanwen.email>
 * @modified 2018-05-08
 */
#include <node.h>
#include "Hash.h"

// #include <iostream>

namespace hash {

using v8::Local;
using v8::Object;

void InitAll(Local<Object> exports) {
  Hash::Init(exports);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)

}  // namespace hash

binding.gyp

{
  "targets": [
    {
      "target_name": "yw_hash",
      "sources": [
        "Hash.cc",
        "yw_hash.cc"
      ]
    }
  ]
}

构建:

$ node-gyp configure
$ node-gyp build

使用方式:
use_hash.js

#!/usr/bin/env node

const process = require('process');
const { Hash } = require('./build/Release/yw_hash');

const ARGV_1 = process.argv[2];
console.log(`Jenkins hash value of '${ARGV_1}': ${ new Hash(ARGV_1).jenkins() }`);

参考:

  1. https://nodejs.org/dist/latest-v10.x/docs/api/addons.html
  2. https://en.wikipedia.org/wiki/Jenkins_hash_function

作者: YanWen

Web 开发者

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s