Showing preview only (9,802K chars total). Download the full file or copy to clipboard to get everything.
Repository: zy445566/myBlog
Branch: master
Commit: 481775ff6e42
Files: 205
Total size: 9.2 MB
Directory structure:
gitextract_mndo1btf/
├── .gitignore
├── LICENSE
├── README.md
├── SUMMARY.md
└── article/
├── ai/
│ ├── README.md
│ └── tfjs-learn/
│ └── README.md
├── block/
│ ├── README.md
│ ├── bitcoin-key-gen/
│ │ └── README.md
│ ├── ecdsa-secp256k1/
│ │ └── README.md
│ ├── fitblock-white-paper/
│ │ └── README.md
│ ├── ipfs-base/
│ │ └── README.md
│ └── publish-coin/
│ ├── Coin.sol
│ └── README.md
├── design-pattern/
│ ├── README.md
│ └── design-pattern-knowledge/
│ └── README.md
├── docker/
│ ├── README.md
│ └── wsl-remote-docker/
│ └── README.md
├── front/
│ ├── README.md
│ ├── make-full-stack-framework/
│ │ ├── README.md
│ │ └── full-stack-demo/
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── back/
│ │ │ │ ├── router.js
│ │ │ │ ├── src/
│ │ │ │ │ └── controller/
│ │ │ │ │ └── HelloController.js
│ │ │ │ └── static.js
│ │ │ ├── config.js
│ │ │ ├── front/
│ │ │ │ ├── run.js
│ │ │ │ ├── src/
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── static/
│ │ │ │ │ └── index.html
│ │ │ │ └── webpack.config.js
│ │ │ └── middleware/
│ │ │ ├── application-type.js
│ │ │ ├── router.js
│ │ │ └── static.js
│ │ ├── main.js
│ │ └── package.json
│ └── whyneedframework/
│ ├── README.md
│ └── example/
│ ├── README.md
│ ├── app.js
│ ├── components/
│ │ ├── AddList.js
│ │ ├── DataBind.js
│ │ ├── MyBrowseRoute.js
│ │ └── MyRouter.js
│ ├── element-registry.js
│ ├── index.html
│ └── package.json
├── git/
│ ├── README.md
│ ├── git-fake/
│ │ └── README.md
│ └── gitmodules/
│ └── README.md
├── http/
│ ├── README.md
│ ├── http-chunked/
│ │ └── README.md
│ └── node-web-socket/
│ └── README.md
├── leetcode/
│ ├── README.md
│ ├── best-time-to-buy-and-sell-stock/
│ │ ├── README.md
│ │ └── best-time-to-buy-and-sell-stock.js
│ ├── bloom-filter/
│ │ └── README.md
│ ├── build-tree/
│ │ ├── README.md
│ │ └── code/
│ │ ├── list.js
│ │ └── tree.js
│ ├── dynamic-programming/
│ │ └── README.md
│ ├── sudoku-solver/
│ │ ├── README.md
│ │ └── sudokuSolver.js
│ └── three-sum/
│ ├── README.md
│ ├── package.json
│ └── treeSumBenchmark.js
├── linux/
│ ├── README.md
│ ├── centos-danger/
│ │ └── README.md
│ └── rsyslog/
│ └── README.md
├── llvm/
│ ├── .gitignore
│ ├── .vscode/
│ │ └── settings.json
│ ├── README.md
│ ├── how_to_ml/
│ │ └── README.md
│ ├── jsvm_c/
│ │ ├── README.md
│ │ ├── fibo.js
│ │ ├── jsvm.cpp
│ │ ├── jsvm.h
│ │ └── main.cpp
│ └── jsvm_js/
│ ├── .vscode/
│ │ └── settings.json
│ ├── README.md
│ ├── fibo
│ ├── fibo.js
│ ├── fibo.js.bc
│ ├── fibo.s
│ ├── index.js
│ ├── lib/
│ │ ├── jsvm.js
│ │ └── parse.js
│ ├── package.json
│ ├── printDouble.cpp
│ └── printDouble.s
├── node/
│ ├── README.md
│ ├── block-run/
│ │ ├── README.md
│ │ ├── index.js
│ │ ├── package.json
│ │ └── test.sql
│ ├── danger-arguments/
│ │ └── README.md
│ ├── deno/
│ │ ├── README.md
│ │ ├── build/
│ │ │ └── README.md
│ │ ├── exends/
│ │ │ └── README.md
│ │ └── frame/
│ │ ├── README.md
│ │ └── jsFrame/
│ │ ├── README.md
│ │ ├── config.js
│ │ ├── controller/
│ │ │ └── HelloController.js
│ │ ├── engineMiddle/
│ │ │ ├── deno/
│ │ │ │ ├── file.js
│ │ │ │ ├── http.js
│ │ │ │ └── mod.js
│ │ │ └── node/
│ │ │ ├── custom-loader.mjs
│ │ │ ├── file.js
│ │ │ ├── http.js
│ │ │ └── index.js
│ │ ├── main.js
│ │ ├── router.js
│ │ ├── run.sh
│ │ └── server/
│ │ ├── HelloServer.js
│ │ └── db.json
│ ├── eventloop/
│ │ └── README.md
│ ├── left-shift-operator-keng/
│ │ └── README.md
│ ├── memory-leak/
│ │ ├── README.md
│ │ ├── actual/
│ │ │ └── README.md
│ │ ├── require-vm/
│ │ │ ├── README.md
│ │ │ ├── case1-require-vm.js
│ │ │ ├── case1-require.js
│ │ │ ├── case1.js
│ │ │ ├── case2-require-vm.js
│ │ │ ├── case2-require.js
│ │ │ ├── case3.js
│ │ │ └── package.json
│ │ └── vm-save-leak/
│ │ └── README.md
│ ├── mmap/
│ │ ├── README.md
│ │ └── share-object/
│ │ ├── README.md
│ │ ├── binding.gyp
│ │ ├── build/
│ │ │ ├── Makefile
│ │ │ ├── Release/
│ │ │ │ ├── .deps/
│ │ │ │ │ └── Release/
│ │ │ │ │ ├── obj.target/
│ │ │ │ │ │ └── shareObject/
│ │ │ │ │ │ └── shareObject.o.d
│ │ │ │ │ └── shareObject.node.d
│ │ │ │ ├── obj.target/
│ │ │ │ │ └── shareObject/
│ │ │ │ │ └── shareObject.o
│ │ │ │ └── shareObject.node
│ │ │ ├── binding.Makefile
│ │ │ ├── config.gypi
│ │ │ ├── gyp-mac-tool
│ │ │ └── shareObject.target.mk
│ │ ├── package.json
│ │ ├── share-data
│ │ ├── shareObject.cc
│ │ ├── shareRead.js
│ │ └── shareWrite.js
│ ├── multi-threaded/
│ │ └── README.md
│ ├── ncpu-resue-threaded/
│ │ └── README.md
│ ├── node-debugger/
│ │ └── README.md
│ ├── node-rust-bindings/
│ │ ├── .gitignore
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── fib/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── lib/
│ │ │ └── index.js
│ │ ├── native/
│ │ │ ├── Cargo.toml
│ │ │ ├── build.rs
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── package.json
│ ├── npmtaobaobye/
│ │ └── README.md
│ ├── opencv-getface.js/
│ │ ├── README.md
│ │ └── example/
│ │ ├── haarcascade_frontalface_default.xml
│ │ ├── index.html
│ │ ├── node-index.js
│ │ ├── opencv.js
│ │ ├── package.json
│ │ └── utils.js
│ ├── quickjs/
│ │ ├── README.md
│ │ └── touch/
│ │ └── README.md
│ ├── stream-question/
│ │ └── README.md
│ ├── type-pointer/
│ │ └── README.md
│ ├── why-await-not-block/
│ │ └── README.md
│ └── whyheighqps/
│ ├── README.md
│ ├── TestServlet.java
│ └── test.js
├── serverless/
│ ├── README.md
│ ├── first-use/
│ │ ├── README.md
│ │ └── test.js
│ └── knative-faas/
│ ├── README.md
│ ├── hello-world/
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── index.js
│ │ ├── package.json
│ │ └── service.yaml
│ └── op.md
├── shadowsocks/
│ ├── README.md
│ └── shadowsocks-rust/
│ └── README.md
├── v8/
│ ├── README.md
│ ├── add_http/
│ │ ├── README.md
│ │ ├── http.cc
│ │ ├── http.h
│ │ ├── http.js
│ │ ├── lastBUILD.gn
│ │ └── zy_node.cc
│ ├── ninja_v8/
│ │ └── README.md
│ └── run_js/
│ ├── README.md
│ ├── lastBUILD.gn
│ └── zy_node.cc
├── wasm/
│ ├── README.md
│ └── emscripten-calling-c/
│ ├── README.md
│ ├── add.c
│ ├── add.js
│ ├── add.wasm
│ └── test.js
└── windows/
├── README.md
└── choco/
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Mac
.DS_store
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# vscode
/.vscode/settings.json
20180825llvm/llvm_js/fibo.js.s
20180825llvm/llvm_js/fibo
20180825llvm/llvm_js/fibo.s
20180825llvm/llvm_js/fibo.js.bc
20180825llvm/llvm_js/printDouble.s
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 ZhangYuan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# myBlog
我的历程
# 目录
- [Introduction](./README.md)
- [人工智能](./article/ai/README.md)
- [机器学习的概念简单手记(20181121)](./article/ai/tfjs-learn/README.md)
- [区块链](./article/block/README.md)
- [Bitcoin 公私钥是如何生成的(20190529)](./article/block/bitcoin-key-gen/README.md)
- [觉醒 js 计算能力,浅谈加密学之椭圆加密算法(20190601)](./article/block/ecdsa-secp256k1/README.md)
- [FitBlock:使用纯 Node.JS 实现的点对点电子现金系统(20200309)](./article/block/fitblock-white-paper/README.md)
- [在新的一年,我做了一个违背链圈的决定,把发币的教程手把手教给大家(20230120)](./article/block/publish-coin/README.md)
- [前端](./article/front/README.md)
- [全栈(gan)工程师第一步:搭建一个全栈(gan)框架(20190824)](./article/front/make-full-stack-framework/README.md)
- [为什么我们还要使用前端框架来构建页面?(20191023)](./article/front/whyneedframework/README.md)
- [Git](./article/git/README.md)
- [github 假面技术(20180929)](./article/git/git-fake/README.md)
- [如何解决多仓库项目的使用问题(20210525)](./article/git/gitmodules/README.md)
- [HTTP 协议](./article/http/README.md)
- [为什么在 HTTP 的 chunked 模式下不需要设置长度(20190515)](./article/http/http-chunked/README.md)
- [使用 nodejs 实现服务端 websocket 通讯(20200409)](./article/http/node-web-socket/README.md)
- [算法](./article/leetcode/README.md)
- [动态规划包 quick-dp 的拆解和使用(20180531)](./article/leetcode/dynamic-programming/README.md)
- [三数之和的优化之旅(20180926)](./article/leetcode/three-sum/README.md)
- [解数独(20181023)](./article/leetcode/sudoku-solver/README.md)
- [用买卖股票的最佳时机理解动态规划(20181220)](./article/leetcode/best-time-to-buy-and-sell-stock/README.md)
- [一道算法题引发布隆过滤器原理的思考(20210326)](./article/leetcode/bloom-filter/README.md)
- [分享一个构建树的方法(20240322)](./article/leetcode/build-tree/README.md)
- [Linux](./article/linux/README.md)
- [教你一步一步利用 rsyslog 搭建自己的日志系统(20170808)](./article/linux/rsyslog/README.md)
- [警惕 Linux 发行版危机和即将或已经发生的重大影响(20230221)](./article/linux/centos-danger/README.md)
- [LLVM](./article/llvm/README.md)
- [利用 LLVM 实现 JS 的编译器,创造属于自己的语言(20180825)](./article/llvm/jsvm_c/README.md)
- [使用 JS 实现 JS 编译器,并将目标 js 生成二进制(20180908)](./article/llvm/jsvm_js/README.md)
- [如何自己创建一种编程语言(20190315)](./article/llvm/how_to_ml/README.md)
- [Node.js](./article/node/README.md)
- [教你一步一步使用 rust 写 js 扩展(20170721)](./article/node/node-rust-bindings/README.md)
- [nodejs 面向指针编程(20180402)](./article/node/type-pointer/README.md)
- [针对使用非块运行和块运行并发压测对比(20180414)](./article/node/block-run/README.md)
- [又被 node 的 eventloop 坑了,这次是 node 的锅(20190115)](./article/node/eventloop/README.md)
- [为什么说 node 具有高并发优势(20190506)](./article/node/whyheighqps/README.md)
- [如何使用 node.js 实现内存共享(20191107)](./article/node/mmap/README.md)
- [警惕 JS 数组解构转参数导致爆栈问题(20200508)](./article/node/danger-arguments/README.md)
- [使用 WebAssembly 版本 opencv 实现人脸识别(20200508)](./article/node/opencv-getface.js/README.md)
- [如何更爽的在 JS 中使用多线程(20200907)](./article/node/multi-threaded/README.md)
- [使用 ncpu 解锁真·多线程复用(20201023)](./article/node/ncpu-resue-threaded/README.md)
- [使用 inspect 进行远程调试(20201104)](./article/node/node-debugger/README.md)
- [为什么说 javascript 的 await 是交出控制权而不是阻塞(20210407)](./article/node/why-await-not-block/README.md)
- [内存泄露相关(20180517)](./article/node/memory-leak/README.md)
- [实战线上内存泄漏问题(20190515)](./article/node/memory-leak/actual/README.md)
- [利用 vm 解决复杂的内存泄漏问题(20200628)](./article/node/memory-leak/vm-save-leak/README.md)
- [利用 require-vm 实现一个更安全的引用,更好的防止内存泄漏篇(20200702)](./article/node/memory-leak/require-vm/README.md)
- [deno 相关(20190125)](./article/node/deno/README.md)
- [编译 deno,deno 结构解析(20190125)](./article/node/deno/build/README.md)
- [deno 系列第二篇,给 deno 做 rust 扩展(20190201)](./article/node/deno/exends/README.md)
- [如何构建一个同时支持 node 和 deno 的框架(20190305)](./article/node/deno/frame/README.md)
- [quickjs 相关(20180517)](./article/node/quickjs/README.md)
- [quickjs 初体验(20191029)](./article/node/quickjs/touch/README.md)
- [淘宝 npm 镜像过期引发的问题(20240125)](./article/node/npmtaobaobye/README.md)
- [小记 Node.js 关于文件描述符的坑(20240617)](./article/node/stream-question/README.md)
- [v8 引擎](./article/v8/README.md)
- [V8 的编译实战](./article/v8/ninja_v8/README.md)
- [利用 v8 引擎实现运行 js 文件](./article/v8/run_js/README.md)
- [给自己的 JS 引擎插上 HTTP 的翅膀](./article/v8/add_http/README.md)
- [serverless](./article/serverless/README.md)
- [Node.js 的 CPU 密集计算的终结解决方案(20171214)](./article/serverless/first-use/README.md)
- [个人搭建 serverless 架构快速指南(20191010)](./article/serverless/knative-faas/README.md)
- [wasm](./article/wasm/README.md)
- [使用 emscripten 实现 js 直接调用 C 代码(20181024)](./article/wasm/emscripten-calling-c/README.md)
- [windows](./article/windows/README.md)
- [windows 开发烦恼之 win10 的神器 choco(20190715)](./article/windows/choco/README.md)
================================================
FILE: SUMMARY.md
================================================
# Summary
- [Introduction](./README.md)
- [人工智能](./article/ai/README.md)
- [机器学习的概念简单手记(20181121)](./article/ai/tfjs-learn/README.md)
- [区块链](./article/block/README.md)
- [Bitcoin 公私钥是如何生成的(20190529)](./article/block/bitcoin-key-gen/README.md)
- [觉醒 js 计算能力,浅谈加密学之椭圆加密算法(20190601)](./article/block/ecdsa-secp256k1/README.md)
- [FitBlock:使用纯 Node.JS 实现的点对点电子现金系统(20200309)](./article/block/fitblock-white-paper/README.md)
- [在新的一年,我做了一个违背链圈的决定,把发币的教程手把手教给大家(20230120)](./article/block/publish-coin/README.md)
- [前端](./article/front/README.md)
- [全栈(gan)工程师第一步:搭建一个全栈(gan)框架(20190824)](./article/front/make-full-stack-framework/README.md)
- [为什么我们还要使用前端框架来构建页面?(20191023)](./article/front/whyneedframework/README.md)
- [Git](./article/git/README.md)
- [github 假面技术(20180929)](./article/git/git-fake/README.md)
- [如何解决多仓库项目的使用问题(20210525)](./article/git/gitmodules/README.md)
- [HTTP 协议](./article/http/README.md)
- [为什么在 HTTP 的 chunked 模式下不需要设置长度(20190515)](./article/http/http-chunked/README.md)
- [使用 nodejs 实现服务端 websocket 通讯(20200409)](./article/http/node-web-socket/README.md)
- [算法](./article/leetcode/README.md)
- [动态规划包 quick-dp 的拆解和使用(20180531)](./article/leetcode/dynamic-programming/README.md)
- [三数之和的优化之旅(20180926)](./article/leetcode/three-sum/README.md)
- [解数独(20181023)](./article/leetcode/sudoku-solver/README.md)
- [用买卖股票的最佳时机理解动态规划(20181220)](./article/leetcode/best-time-to-buy-and-sell-stock/README.md)
- [一道算法题引发布隆过滤器原理的思考(20210326)](./article/leetcode/bloom-filter/README.md)
- [分享一个构建树的方法(20240322)](./article/leetcode/build-tree/README.md)
- [Linux](./article/linux/README.md)
- [教你一步一步利用 rsyslog 搭建自己的日志系统(20170808)](./article/linux/rsyslog/README.md)
- [警惕 Linux 发行版危机和即将或已经发生的重大影响(20230221)](./article/linux/centos-danger/README.md)
- [LLVM](./article/llvm/README.md)
- [利用 LLVM 实现 JS 的编译器,创造属于自己的语言(20180825)](./article/llvm/jsvm_c/README.md)
- [使用 JS 实现 JS 编译器,并将目标 js 生成二进制(20180908)](./article/llvm/jsvm_js/README.md)
- [如何自己创建一种编程语言(20190315)](./article/llvm/how_to_ml/README.md)
- [Node.js](./article/node/README.md)
- [教你一步一步使用 rust 写 js 扩展(20170721)](./article/node/node-rust-bindings/README.md)
- [nodejs 面向指针编程(20180402)](./article/node/type-pointer/README.md)
- [针对使用非块运行和块运行并发压测对比(20180414)](./article/node/block-run/README.md)
- [又被 node 的 eventloop 坑了,这次是 node 的锅(20190115)](./article/node/eventloop/README.md)
- [为什么说 node 具有高并发优势(20190506)](./article/node/whyheighqps/README.md)
- [如何使用 node.js 实现内存共享(20191107)](./article/node/mmap/README.md)
- [警惕 JS 数组解构转参数导致爆栈问题(20200508)](./article/node/danger-arguments/README.md)
- [使用 WebAssembly 版本 opencv 实现人脸识别(20200508)](./article/node/opencv-getface.js/README.md)
- [如何更爽的在 JS 中使用多线程(20200907)](./article/node/multi-threaded/README.md)
- [使用 ncpu 解锁真·多线程复用(20201023)](./article/node/ncpu-resue-threaded/README.md)
- [使用 inspect 进行远程调试(20201104)](./article/node/node-debugger/README.md)
- [为什么说 javascript 的 await 是交出控制权而不是阻塞(20210407)](./article/node/why-await-not-block/README.md)
- [内存泄露相关(20180517)](./article/node/memory-leak/README.md)
- [实战线上内存泄漏问题(20190515)](./article/node/memory-leak/actual/README.md)
- [利用 vm 解决复杂的内存泄漏问题(20200628)](./article/node/memory-leak/vm-save-leak/README.md)
- [利用 require-vm 实现一个更安全的引用,更好的防止内存泄漏篇(20200702)](./article/node/memory-leak/require-vm/README.md)
- [deno 相关(20190125)](./article/node/deno/README.md)
- [编译 deno,deno 结构解析(20190125)](./article/node/deno/build/README.md)
- [deno 系列第二篇,给 deno 做 rust 扩展(20190201)](./article/node/deno/exends/README.md)
- [如何构建一个同时支持 node 和 deno 的框架(20190305)](./article/node/deno/frame/README.md)
- [quickjs 相关(20180517)](./article/node/quickjs/README.md)
- [quickjs 初体验(20191029)](./article/node/quickjs/touch/README.md)
- [淘宝 npm 镜像过期引发的问题(20240125)](./article/node/npmtaobaobye/README.md)
- [小记 Node.js 关于文件描述符的坑(20240617)](./article/node/stream-question/README.md)
- [v8 引擎](./article/v8/README.md)
- [V8 的编译实战](./article/v8/ninja_v8/README.md)
- [利用 v8 引擎实现运行 js 文件](./article/v8/run_js/README.md)
- [给自己的 JS 引擎插上 HTTP 的翅膀](./article/v8/add_http/README.md)
- [serverless](./article/serverless/README.md)
- [Node.js 的 CPU 密集计算的终结解决方案(20171214)](./article/serverless/first-use/README.md)
- [个人搭建 serverless 架构快速指南(20191010)](./article/serverless/knative-faas/README.md)
- [wasm](./article/wasm/README.md)
- [使用 emscripten 实现 js 直接调用 C 代码(20181024)](./article/wasm/emscripten-calling-c/README.md)
- [windows](./article/windows/README.md)
- [windows 开发烦恼之 win10 的神器 choco(20190715)](./article/windows/choco/README.md)
================================================
FILE: article/ai/README.md
================================================
# 人工智能
* [机器学习的概念简单手记(20181121)](../ai/tfjs-learn/README.md)
================================================
FILE: article/ai/tfjs-learn/README.md
================================================
# 一个JS程序员对机器学习的概念简单手记
为什么要学习机器学习,我认为有以下重要的三点:
* 可缩短我们的编程时间,比如可以通过机器学习学习垃圾话样本,进行更快速更精准的垃圾话的检测
* 普通编程方法难以解决的问题,比如用户潜在喜好和用户行为的预测
* 更重要的是扩宽我们程序员的思维逻辑,对于适用的方向能够提出这方面的构思
从前JS程序员要学习机器学习,总是困难的,很多时候被算法和复杂的逻辑所困住,但现在问题得到很大的缓解,我们可以用tensorflow.js(训练和部署机器语言模型的JavaScript库)提供的库和用更好的方式来更简单的实现机器学习能力。
本文将主要讲解机器学习的一些主要概念。更偏重适用tensorflow.js的实战的入门级教程,[请点击这里(前三节强烈推荐)](https://github.com/zy445566/tfjs-tutorials-zh/blob/master/README.md)。
# 基本的机器学习的流程一般是怎么样的
在学习机器学习前我认为首先要明确以下几点
* 标签 一般来说标签就是预测目标的结果,也可以是预测的正确值。
* 特征 一般是指提提供训练的样本,而样本分以下几类:
* 有标签的样本(带正确答案的样本,大部分都是使用有标签的样本进行训练)
* 无标签的样本(不带正常答案的样本)
* 模型 指的的是预测和训练样本的工具。你可以形象的理解为婴儿的大脑。
* 损失函数 用于计算标签和模型预测值的差值。
* 优化器 用于将损失函数计算的差值向正确方向优化的步伐
* 学习速度 一般代表优化器的优化的步伐大小,过大则容易偏离正确值,过小则要更多运算才能到达正确值。就好比你要到马路中间要走5米,然而一次走500米和一次走5厘米,都很难到达马路中间。
好了,知道了以上几种概念,那么我们来以一张图的方式来展示,机器到底是如何进行学习的。

那么从图中很容易了解到,我们是将特征输入到模型中计算出预测值,将标签进行通过损失函数计算出误差值,再交给优化器优化,参数更新后,模型再重复这一个过程,就构成了基本的机器学习的流程。
# 过拟合
为什么要讲过拟合?什么是过拟合?
防止过拟合是分类器的一个核心任务。而比如人脸识别,实际上就是一个分类器,比如将图片的风景,动物脸,人脸中的人脸的特征进行归类,将欧美人脸,亚太人脸,非洲人脸进行归类,甚至可以将某个特定的人的脸单独归为一类。所以它在机器学习里面也有举足轻重的地位。
那什么是过拟合呢?举个例子,我们在日常生活中见到的羊都是白色的,那有一天看到了除了颜色是黑色其他特征和我们日常见到的白羊都是一样的,那我们是不是就会认为这不是一只羊,人当然不会因为颜色就断定这不是一只羊,而机器却会说我见过的羊都是白色的,所以不可能有黑色的羊,所以这不是一只羊。过拟合官方的解释是为了得到一致假设而使假设变得过度严格。
那么过拟合要如何解决呢?这是一个机器学习重要的能力:泛化。
那么如何保证泛化,经验告诉我们有3个要点:
* 从分布中抽取`独立同分布`(iid)样本
* 分布是`平稳的`不会随时间变化而变化
* 始终从`同一分布`抽取样本
但样本的使用也同样重要,一般我们会将样本分为 训练集,验证集,测试集。
各自用途是什么?为什么需要分3个集?
用途是什么,这个问题很简单:
* 训练集 用于训练模型
* 验证集 用于评估模型,即在训练每轮后验证训练的准确性,并帮助矫正参数
* 测试集 用于测试模型,验证模型的准确性
为什么需要分3个集?可能大家会觉得为什么要三个集,直接用测试集评估和测试模型不就好了。
那我们做一个假设。如果我们用测试集评估模型,然后调整参数的话如下图:

那么会不会出现之前所说`过拟合`的问题呢?答案是会的。即为了得到一致假设而使假设变得过度严格,请仔细思考这句话。
而正确的方式应该是:

使用这样的流程就不会产生因为测试数据加入训练,导致通过了最终的测试数据中。
# 处理源数据的技巧
在训练模型之前我们需要处理大量的源数据同时转换为我们模型可以使用的数据,那么源数据的处理技巧就至关重要了。
在将原始数据处理成特征的这个过程,我们叫特征工程。
那么我们在做特征工程时有什么技巧:
* 字符串可以使用独热编码
* 过滤非合理数据,比如库中极少的数据
* 筛选不随时间变化的特征
* 使用分箱技巧处理非线性性特征
当然我们在处理这类数据时,也应保持以下几点以更容易暴露有问题的数据:
* 将数据可视化呈现
* 不断地对数据进行调试
* 对数据进行监控
讲了数据处理,我们讲讲扩展数据集的方法,那么什么是扩展数据集呢?比如我们的数据集不是很充足,比如只有10个,显然是不足以训练模型的数据集,目前最主流的方法是可以通过它的单一特征来实现扩展,比如都在某个城市出现过,这种是线性的扩展方式。但是
而对数据的处理中很多特征,不是通过简单的单一线性就能划分的特征,比如在某个城市出现过的且短期存在的特征,这个就是非线性特征,我们需要短期存在和某个城市出现两个特征一起查询数据,这样的过程叫做特征交叉,即将两个数据交叉后转换为线性的方法。目前的事实也证明通过通过线性扩展数据是最高效的方法。(个人觉得说特征交叉就高大上一点,而两个条件相交查询就不专业了)
# 如何让预测结果更加正常和正确评估模型
在之前说过我们的模型训练需要通过损失函数计算损失,那么我们如何降低更多的损失呢?就比如我们训练集的损失度通过不断的学习越来越低,而测试集在训练集损失最低的时刻并不能是最低点,甚至说训练集样本损失度更高的时候,测试集损失度反而更低,我们应该如何解决这个问题。让机器学习更好泛化到新样本中呢?
解决方案:
* 早停法,可以简单理解为用测试集测试的损失度最低点时就停止训练的模型
* 正则化
第一种方案在实际的操作中比较难控制,那么我们今天主要来讲讲主要使用的L2(岭回归)正则化,即以下几点。
* 定义复杂度(模型)= 权重的平方和
* 使用适当的权重
* 对于线性模型:首选比较平缓的斜率
* 贝叶斯先验概率:
* 权重应该以 0 为中心
* 权重应该呈正态分布
除L2正则化还存在L0正则化,L0正则化主要是解决:
* 稀疏特征组合造成内存消耗过大
* 噪点过多的问题
那么L0,和L2的区别是什么呢?即将权重设为0,处理稀疏特征组合数据。L10属于非凸优化问题。
那么L0可能过于暴力,会导致NP-hard的问题,但我们还有一个折中的方案L1,即只对权重绝对值之和进行惩罚。同时L1属于凸优化问题。
通常我们可以使用逻辑回归结合一起使用
例如在求解概率的问题上,我们可以使用线性逻辑回归来快速求解问题,在非线性的情况,我们可以使用上节提到的特征交叉的方法来转换成线性再进行线性逻辑回归来快速求解问题,同时引入早停法和正则化来防止过拟合。
例如之前说过人脸识别实际上是一个分类问题,我们就可以使用逻辑回归来判断分类概率,同时设置阈值来判断是否加入该分类,有时在这方面准确率产生也会产生误导性,如:
* 在不同类型的问题需要不同的解决方案时
* 分类不平衡,即正类别或负类别极其罕见时
解决分类不平衡,我觉得我们需要先了解下真正例和假正例,官方说的很好,我直接把这个狼来了的故事搬过来。
* 真正例 我们正确地提醒了狼的出现!我们拯救了小镇。
* 假正例 错误:我们错误地提醒了狼的出现。所有人都对我们非常生气。
* 假负例 确实有一头狼出现了,但我们没有发现它。狼吃光了我们所有的鸡。
* 假负例 没有狼出现,也没有提醒。大家都相安无事。
我们可以把真正例,假正例,假负例,假负例组成不同的指标
* 精确率:(真正例次数)/(所有正类别预测次数)
* 召回率:(真正例次数)/(所有实际正类别数)
精确率和召回率往往处于此消彼长的状态,精确率和召回率是模型的重要评估指标,但往往我们无法直接得出精确率和召回率,所以可以使用AUC(ROC曲线下方的面积大小)来解决这个问题,即随机挑选一个正样本以及一个负样本,当前的分类算法根据计算得到的Score值将这个正样本排在负样本前面的概率就是AUC值,而这个AUC值越高,模型就越好。
还有一个重要评估的指标是预测偏差,即我们所有预测项的总和和观察项的总和比较下的结果。但偏差为0也并不能说明模型就完美,但预测偏差是个很好的checklist项目。
# 深度神经网络
在处理源数据的技巧的小节上说到,对于非线性特征可以使用特征交叉的方法来转换成线性特征来解决,但对于十分复杂的特征值,比如说真正例和假正例很接近,该如何处理呢?对的,可以使用深度神经网络来解决这个问题。
对于线性问题,我们的层级大概只有两层,即输入层和输出层
但对于非线性问题,我们往往需要增加一些层,通常通过ReLU(线性整流函数)和BP(反向传播算法)来实现。
对于ReLU可以计算是否线性,一般来说大于0则为线性问题
对于BP来说,常见的方法有SGD(随机梯度下降法),算法由主要由两个阶段组成:激励传播与权重更新。
#### 第1阶段:激励传播
每次迭代中的传播环节包含两步:
* (前向传播阶段)将训练输入送入网络以获得激励响应;
* (反向传播阶段)将激励响应同训练输入对应的目标输出求差,从而获得隐藏层和输出层的响应误差。
#### 第2阶段:权重更新
对于每个突触上的权重,按照以下步骤进行更新:
* 将输入激励和响应误差相乘,从而获得权重的梯度;
* 将这个梯度乘上一个比例并取反后加到权重上。
这个比例(百分比)将会影响到训练过程的速度和效果,因此成为“训练因子”。梯度的方向指明了误差扩大的方向,因此在更新权重的时候需要对其取反,从而减小权重引起的误差。
第 1 和第 2 阶段可以反复循环迭代,直到网络对输入的响应达到满意的预定的目标范围为止。
注意事项:
* 梯度很重要
* 如果它是可微的,则我们才能够对其进行学习
* 梯度可能会消失
* 每个额外的层都会依次降低信噪比
* ReLu 在这里很有用
* 梯度可能会分解(比如妈妈里面出现男,其实这在中国很常见)
* 学习速率在这里很重要
* 批标准化(实用按钮)可以提供帮助
* ReLu 层可能会消失
* 保持冷静,并降低您的学习速率
如何解决该问题,标准化特征值很重要
* 特征具有合理的范围
* 大致以 0 为中心,[-1, 1] 的范围通常效果比较好
* 有助于梯度下降法收敛;避免 NaN 陷阱
* 避免离群值也会有帮助
* 可以使用一些标准方法:
* 线性缩放
* 为最大值和最小值设定硬性上下限(截断)
* 对数缩放
其次在深度神经网络里面还有一个很有用的技巧,丢弃
* 丢弃:另一种正则化形式,对神经网络很有用
* 工作原理是,在一个梯度步长中随机“丢弃”网络的单元
* 有一个可用于集成学习此处的模型的连接
* 丢弃得越多,正则化效果就越强
* 0.0(一点都不丢弃) = 无丢弃正则化,得到原来的复杂模型
* 1.0 (全部丢弃) = 丢弃所有内容!学不到任何规律,得到特别简单且无用的模型
* 中间值更有用(在这个位置进行丢弃,则是在这个位置应用了有效的正则化)
# 多类别神经网络
在之前的章节里面说到,逻辑回归很适合一些是或者不是的问题,它可以很方便给某样东西的是是或不是加上概率,比如是否是是垃圾邮件还是非垃圾邮件。
但在多类别中,我们是怎么处理的呢,即如何处理狗是哺乳动物?还是植物?还是其他什么?这种多分支选择。
那么我们在一对多类别中,我们可以:
* 为每个可能的类别创建唯一输出
* 分别对“我的类别”与“所有其他类别”信号进行训练
* 可以在深度网络中执行,也可以借助单独的模型执行
那我们有没有更好,更方便的方法呢?对的,我们可以使用SoftMax进行多类别划分。SoftMax使得:
* 添加了附加限制:要求所有一对多节点的输出总和为 1.0(100%,这就是逻辑回归的多类别升级版)
* 附加限制有助于快速训练收敛
* 另外,允许输出解析为概率
我们可以使用两种方式让SoftMax进行计算
* 全部数据计算 即暴力破解;针对所有类别进行计算
* 采样数据计算 即针对所有正类别标签进行计算,但仅针对负类别标签的随机样本进行计算。
那我们应该如何使用标签给多类别分类呢?
多类别单一标签分类:
* 一个样本可能只是一个类别的成员。
* 类别互斥这一限制是有用的结构。
* 有助于在损失中对此进行编码。
* 将一个 softmax 损失用于所有可能的类别。
多类别多标签分类:
* 一个样本可能是多个类别的成员。
* 无需对类别成员资格设定额外的限制。
* 将一个逻辑回归损失用于每个可能的类别。
# 相似性神经网络
这个在商品推荐和广告推荐比较常用。通常我们需要在多维度分析用户来推荐对应商品:
* 比如用户对商品的兴趣可大致从N个方面分析
* 每部商品都变成一个点,在不同的维度中,这个点的高度都是不一样的
* 那么我们可从数据中嵌套学习,用户在不同维度的兴趣点
在深度网络中学习嵌套能做什么?
* 无需单独的训练过程,也就是说,嵌套层只是隐藏层,每个维度一个单元
* 监督式信息针对所需任务调整学到的嵌套,即根据用户上一个商品调整,下一个商品出现
* 隐藏单元直观地发现整理多维空间中的各项,才能最大限度地优化最终目标
我们可以使用输入表示法来显示用户用户兴趣的相同特征
* 每个样本是用户兴趣的相同的特征的稀疏矢量
* 使矩阵样本的密集表示法表示
比如
|x|双肩包|沙发|冰箱|单肩包|
|---|---|---|---|---|
|x|x|v|x|x|
|x|x|x|v|x|
|用户|v|x|x|v|
那么用户的矩阵样本可表示为(1,0,0,1),但这种方法从空间和时间上来说,这种表示法并不高效,因为每个看过的商品都是一行,我这里只例举了双肩包,沙发,冰箱,单肩包。而正常来说,用户看的远超这些。
我们如何选择嵌套维度个数,请根据以下三条判断:
* 嵌套维度的个数越多,越能准确地表示输入值之间的关系
* 不过,维度个数越多,过拟合的可能性就越高,训练速度也会越慢
* 经验法则
嵌套能做什么?
* 嵌套会以相似内容彼此靠近的方式将各项内容(如影片、文字等)映射到低维实矢量
* 嵌套也可应用于密集数据(如音频),以创建有意义的相似度指标
* 联合嵌套多种类型的数据(如文字、图片、音频等),以定义这些数据之间的相似度
# 结语
为什么要学习这些概念,我认为概念讲解往往比代码讲解更容易理解一样东西,代码也仅仅是概念的一种实现,概念能更好帮助我们在后续的阶段更好的学习,欢迎[查看tensorflow.js的实战的入门级教程](https://github.com/zy445566/tfjs-tutorials-zh/blob/master/README.md)学习更多实战内容。
================================================
FILE: article/block/README.md
================================================
# 区块链
* [Bitcoin公私钥是如何生成的(20190529)](../block/bitcoin-key-gen/README.md)
* [觉醒js计算能力,浅谈加密学之椭圆加密算法(20190601)](../block/ecdsa-secp256k1/README.md)
* [FitBlock:使用纯Node.JS实现的点对点电子现金系统(20200309)](../block/fitblock-white-paper/README.md)
* [在新的一年,我做了一个违背链圈的决定,把发币的教程手把手教给大家(20230120)](../block/publish-coin/README.md)
================================================
FILE: article/block/bitcoin-key-gen/README.md
================================================
# Bitcoin公私钥是如何生成的
原本一直都是靠比特币钱包生成公私钥的,但感觉一直不是很放心,尤其是npm包隐藏盗币代码后,一直感觉危险重重,加上货币交易所,也是存在倒闭或跑路的风险,毕竟是第三方。加之又看到node12支持了原生的BigInt,想着是时候自己做个无第三方依赖的公私钥生成工具了。
# 先谈私钥生成
私钥是如何产生的呢?简单来说就是在一个大数中选值,最后进行按照一些规则加密成我们所使用的私钥。我这边使用了两种方法实现,一个是随机法,一个是加密法生成。
## 先看加密生成法
```js
// 简易sha256通过字符串生成摘要
function getPrivteOriginKeyByStr(strSeed) {
return crypto.createHash('sha256').update(strSeed).digest('hex');
}
```
这里就是直接使用sha256生成摘要,然后生成一个十六进制的私钥原值
## 再随机生成法
```js
const n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n;
function getPrivteOriginKeyByRand() {
let nHex = n.toString(16);
let privteKeyList = [];
let isZero = true;
for(let i=0;i<nHex.length;i++) {
let rand16Num = Math.round(Math.random()*parseInt(nHex[i],16));
privteKeyList.push(rand16Num.toString(16));
if(rand16Num>0) {isZero = false;}
}
if(isZero){getPrivteOriginKeyByRand();}
return privteKeyList.join('');
}
```
这里就是通过通过每位进行随机,再组合生成一个长串,目前node的随机数种子在linux中会取一个文件的指纹(这个文件是会不断变化的,以前看过代码,现在有点忘记叫什么文件了),所以你不用当心第一次生成都会是一样的值。
## 第二步就是将原值转换成我们能导入钱包的私钥
转换规则是原值加上版本做前缀,进行两次sha256加密,同时取前4个字节,进行58进制转换。如下。
```js
function hex2sha256(hexStr) {
return crypto.createHash('sha256').update(Buffer.alloc(hexStr.length/2,hexStr, 'hex')).digest('hex');
}
function getPrivteKeyByOrigin(privteKeyOrigin) {
if(privteKeyOrigin.length!==64){
throw new Error('privte Key Origin length must be 64!')
}
let version = '80';
let sha1Str = `${version}${privteKeyOrigin}`;
let sha1 = hex2sha256(sha1Str);
let sha2Str = `${sha1}`;
let sha2 = hex2sha256(sha2Str);
let key = `${version}${privteKeyOrigin}${sha2.slice(0,8)}`;
return util.hex2Base58(key);
}
```
这里你可能会有几个疑问:
* 为什么hexStr.length要除以2?
* 因为一个字节是255,两个十六进制数才构成一个字节,所以需要除2,同时如直接使用文本则会直接已默认utf8的对象传入一个十六进制字符占一字节导致计算不准确。
* 那之前说好前4个字节,为什么要sha2.slice(0,8)?
* 和上面一样sha2是16进制数,4字节就是十六进制数的8位。
* 58进制是什么鬼?
* 58进制其实就是数字和大小写字母中剔除了大写i,大写o,小写L,数字0,因为这些无论是被当作地址还是作为私钥都容易被人混淆,所以去除了。数字加大小写共62位,减去4位则为58位。
# 再谈公钥生成
## 第一步公钥的原值
公钥原值生成其实是采用了椭圆加密算法,简单来说就是使用了E : y^2 ≡ x^3 + ax + b (mod p)算法实现椭圆曲线,然后使用K=kG,计算公钥,小k是私钥,大K我们要求的公钥,G是椭圆曲线上的一个点,这是一个常量。
注意的点是kG并不是代表k和G点的乘基,而是又前一个点推导到后一个点。
可以用如下公司求解(其中a,p都是常量):
```
相同的点相加第一式: λ≡(3x1^2+ a)/2y1(mod p)
相同的点相加第二式: x3 ≡ λ^2 − 2x1 (mod p), y3≡ λ(x1 − x3) − y1 (mod p)
不同的点相加第一式: λ≡ (y2 − y1)/(x2 − x1)(mod p)
不同的点相加第二式: x3 ≡ λ^2 − x1 − x2 (mod p), y3 ≡ λ(x1 − x3) − y1 (mod p)
```
第一步:比如G点的x,y坐标是x1,y1,那么这时我需要求解2G,那么先用G导入“相同的点相加第一式”,求出λ,然后“相同的点相加第二式”求解x3,y3,这个点就是2G了。
第二步:现在有G和2G两个点,那么3G的求解则是将2G带入“不同的点相加第一式”去减G,求出λ,然后再用“不同的点相加第二式”求解x3,y3,这个点就是3G了。
然后4G,5G,6G...kG可以不断使用第二步循环执行来得出。
目前本人正在写这部分的原生纯算法实现,但是目前生成公钥因为不需要签名所以,我直接用了node的ECDH库,因为ECDH和ECDSA仅仅是椭圆加密算法的不同实现,所以生成公钥可以直接使用。如下。
```js
function getPublicOriginKey(privteKeyOrigin) {
if(privteKeyOrigin.length!==64){
throw new Error('privte Key Origin length must be 64!')
}
const ecdh = crypto.createECDH('secp256k1');
ecdh.setPrivateKey(privteKeyOrigin,'hex');
return ecdh.getPublicKey('hex');
}
```
## 第二步公钥原值生成公钥地址
这里其实就是将公钥原值,先进行一次hex2sha256运算,然后使用再ripemd160加密上一步结果,将结果增加主网号00后再进行两次加密,取ripemd160加密结果和上一次结果的前四个字节,组成key走一个转58进制,加地址标示1,生成公钥地址。如下。
```js
function getPublicKeyByOrigin(publicKeyOrigin) {
let mainVersionHex = '00';
let addreeSign = '1';
let sha1 = hex2sha256(publicKeyOrigin);
let ripemd160Hex = crypto.createHash('ripemd160').update(Buffer.alloc(sha1.length/2,sha1, 'hex')).digest('hex');
let ripemd160HexUsed =`${mainVersionHex}${ripemd160Hex}`;
let sha2 = hex2sha256(ripemd160HexUsed);
let sha3 = hex2sha256(sha2);
let key = `${ripemd160HexUsed}${sha3.slice(0,8)}`;
return `${addreeSign}${util.hex2Base58(key)}`;
}
```
生成公钥地址可以说是不可逆的,首先用椭圆加密算法将私钥进行了数学难题加密,再通过摘要算法,只获取摘要信息,所以简单来说这个公钥地址也仅仅是原值的摘要而已,连要复原公钥原值都不太可能。
# 附文
欢迎大家使用 [bitcoin-key-generator(代码点此)](https://github.com/zy445566/bitcoin-key-generator)来生成私钥,代码不多,无第三方库,可以看后再生成保证自己私钥安全。最后希望大家能推荐我一个杭州仓前不加班955的坑,工资可谈。
================================================
FILE: article/block/ecdsa-secp256k1/README.md
================================================
# 觉醒js计算能力,浅谈加密学之椭圆加密算法
上一节谈了[《Bitcoin公私钥是如何生成的》](https://github.com/zy445566/myBlog/blob/master/20190529bitcoin/20190529bitcoin-key-gen/README.md),并简单聊了下椭圆加密算法的标量运算,但是很多计算机实现密码学过程中的算法细节都没有谈,今天我们来谈一谈,并且用js的新能力BigInt来从零实现ecdsa-secp256k1。
# 0x01我们要用椭圆加密算法干什么?
可能很多人只知道Bitcoin钱包的核心是椭圆加密算法,但是椭圆加密算法能干什么却不是很清楚。
举个栗子,小明给小红开了一张一百万的支票,小红拿着支票去银行兑换,银行要验证支票是否真伪,首先要看支票是不是由该银行的发行的,第二要看支票的签名是否是本人,确定后才能给予了小红一百万。
在数字世界又该如何实现呢?
* 首先小明通过自己的身份去银行申请到了支票
* 这就像私钥可以通过算法生成公钥。
* 其次小明在支票上签名生成一个有效的支票
* 这就像利用签名算法生成了一个有效签名数据。
* 银行要验证支票是不是由该银行的发行和该签名是否是本人
* 这就像利用验证算法通过公钥和签名数据验证签名是否有效
而这些利用椭圆加密算法就能做到。
# 0x02如何生成私钥又如何产生公钥
`注意:下文提到的p,a,b,G,N都是常量,你可以简单的认为是某个数字`
私钥可以从1到N中选择一个值作为私钥(k),而公钥(K)的算法是K = kG,难道是k乘G?当然不是。这里的乘是标量乘法。
## 那么何为`标量乘法`呢?
简单来说即是kG只能由另外两个点叠加而成。
比如k等于14,我们只知道常量G,那么要叠加到14G是不是要这样(如下):
```
G+G = 2G
2G+G = 3G
3G+G = 4G
...
13G+G=14G
```
那有意思了,那如果k很大要怎么办呢?其实很简单,我们只要对折运算就好了。
先把14转换成二进制即是1110,那是不是相当于是(如下):
```
1110:
=>1*(2^3)G+1*(2^2)G+1*(2^1)G+0*(2^0)G
=> 1*8G + 1*4G + 1*2G + 0*G
那么二进制只有4位就只要先计算4-1次,然后再相加不就好了
G+G = 2G
2G+2G = 4G
4G+4G = 8G
然后
2G+4G = 6G
6G+8G = 14G
```
对比挨个G相加,14要加13次,而下面这种只要相加5次,是不是就少了很多。当然越大的数,少的次数就越多。比如10000要相加9999次,而用优化后的方法10000只要相加17次就能得到结果。这个优化是极度恐怖的。
## 说完标量乘法我们来看看具体G是如何相加的
首先G相加更具推导公司分两种情况,我们用武功秘籍的方法来相称吧,以下(其中x1,y1,x2,y2,x3,y3是坐标变量):
```
相同的点相加第一式: λ≡(3x1^2+ a)/2y1(mod p)
相同的点相加第二式: x3 ≡ λ^2 − 2x1 (mod p), y3≡ λ(x1 − x3) − y1 (mod p)
不同的点相加第一式: λ≡ (y2 − y1)/(x2 − x1)(mod p)
不同的点相加第二式: x3 ≡ λ^2 − x1 − x2 (mod p), y3 ≡ λ(x1 − x3) − y1 (mod p)
```
之前说G是一个数字常量,那么为什么又和坐标产生关系了呢?因为G点就是一个坐标数字,在密码学中常常会把一个坐标根据一定的规律转换成数字。
```
比如G点的十六进制数字值是:
0x0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
而04表示这是一个坐标点的数字标识。
X坐标为前64位:79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Y坐标为后64位:483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
```
按照我们之前的逻辑我们就可以将G点代入“相同的点相加第一式”,接着求出λ后则需要代入“相同的点相加第二式”求出2G,但是这又有一个问题,那就是在第一式中有除法,如果有除法就可能有余,而且可能会无限位,在密码学和包括天文学涉及大数运算都偏向于整数运算,同时小数无限位会存在一个计算机数据难以表示,即使能表示也很难代入下一步进行精确运算。此时保证数学公式计算值的准确性又是一个问题。
## 祭出“乘法逆元”解决相除导致产生小数无限位的问题
什么是“乘法逆元”?简单来说就是将除法转换成乘法的值。举个栗子,假设a/b其实是可以转换成a*(b^-1),这里面的(b^-1)就是我们要求的乘法逆元。经过推导,我们在当取模数为素数时,可以转换成整数形式。而求解乘法逆元最为常见的算法就是通过“欧几里德算法”来求取。代码如下:
```js
// 欧几里德算法求某数的乘法逆元
function inverseMulti(x,modNum) {
let x1= 1n,x2 = 0n,x3 = modNum;
// 取模相加保证求解数一定为正数
let y1 = 0n,y2=1n,y3=(x%modNum+modNum)%modNum;
let q;
let t1,t2,t3;
while(true){
if(y3==0n)throw new Error('multiplicative inverse modulo is no answer!');
if(y3==1n)return y2;
q = x3/y3;
t1=x1-q*y1;t2=x2-q*y2;t3=x3-q*y3;
x1=y1;x2=y2;x3=y3;
y1=t1;y2=t2;y3=t3;
}
}
```
在完成乘法逆元的求解后,我们就可以根据公式很好的完成相同点相加和不同点相加,再通过我之前提到的对折运算来快数获取公钥结果。所以从上面可以知道公钥就是点的叠加,所以公钥的本质也就是一个点。
# 0x03如何进行签名和验证
通过0x02节,我们知道了如何生成私钥和公钥,假设你通过私钥(d)生成了公钥(Q),那么根据椭圆加密算法的特性,通过私钥和另一对秘钥(k(私钥),R(公钥)进行运算后可以生成一个签名,再通过我们的公钥和和另一对公钥可以验证信息的签名是否正确。
根据推导我们先随机生成一对秘钥k,R,再对R点的x坐标进行取模,如果为0,继续取模。再通过s = k^-1 (e + rd) mod n,其中e是签名信息的数字表示,比如签名信息(M)是"This a String"这样的字符串,那么e可以是sha256(M)得到数字摘要。其中d是你的私钥值。n是一个常量,是私钥的最大取值上限。具体实现如下:
```js
function sign(n,pointG,p,a,d,mNum) {
let k,R;
let r = 0n;
while(r==0n) {
// 随机生成私钥
k = getPrivteKeyNumByRand(n);
// 根据私钥计算公钥的点
R = getPointByNum(k,pointG,p,a);
// 此方法即是(R.x%n+n)%n
r = postiveMod(R.x,n);
}
let e = mNum;
let s = postiveMod(((e+(r*d))*inverseMulti(k,n)),n);
if(s==0n) {
return sign(n,pointG,p,a,d,M,encoding);
}
return {r,s};
}
```
根据椭圆加密算法特性,我们可以推导出,当我们计算出R = (es^-1 mod n)G + (rs^-1 mod n)Q 即是计算出签名时的随机的那个公钥值,所以我们只要通过v = R.x mod n 计算出v的值,再判断r是否等于v,即可判断签名是否有效。其中G是常量点,Q为你的公钥。具体实现如下:
```js
function verify(n,pointG,p,a,pointQ,S,mNum) {
let {r,s} = S;
if(!(r>0n && r<n && s>0n && s<n)){return false;}
let e = mNum;
let w = inverseMulti(s,n);
let u1 = postiveMod((e*w),n);
let u2 = postiveMod((r*w),n);
let u1Point = getPointByNum(u1,pointG,p,a);
let u2Point = getPointByNum(u2,pointQ,p,a);
let pointR;
if(u1Point.x==u2Point.x && u1Point.y==u2Point.y) {
// 如果为相同点则进行叠加运算
pointR = addSamePoint(u1Point.x,u1Point.y,p,a);
} else {
// 如果为不相同点则进行相加运算
pointR = addDiffPoint(u1Point.x,u1Point.y,u2Point.x,u2Point.y,p);
}
if(pointR.x==0n && pointR.y==0n) {return false;}
let v = postiveMod(pointR.x,n);
if(v==r) {
return true;
}
return false;
}
```
# 0x04附录
至此,你已经可以通过该算法实现一个简易的钱包功能了,本文由于想要尽可能表达实现过程,给人更多的思路借鉴,所以尽可能少的用代码表达,如有需要请点击算法的实例代码地址查看,算法的实例代码地址:[点此打开ecdsa-secp256k1实例代码](https://github.com/zy445566/ecdsa-secp256k1),希望能对你有所帮助。
================================================
FILE: article/block/fitblock-white-paper/README.md
================================================
# FitBlock:使用纯Node.JS实现的点对点电子现金系统
本文提出通过Node.js实现区块链系统,并可以实现Web在线挖矿。目前系统基本开发完成,将处于测试阶段和最后准备阶段(包括多机可用性测试,文档编写,准备上帝区块,部署引导节点)。而区块链对于大多数开发是神秘的,尤其语言鄙视链的存在,让其它人对于JS开发区块链系统有一定偏见。所以我希望能通过本人开发区块链的一些经历把区块链的技术要点做一些简单的分享。
本文开发区块链源码地址:[FitBlock仓库地址](https://github.com/FitBlock/fit-block)
# 剧前提要
SHA-256是一种摘要算法,和大家熟悉的MD5算法类似,就是将大量或少量数据来生成几乎唯一的一个极大数。但SHA-256长度偏长且摘要算法更加安全。
ecdsa-secp256k1是一种椭圆加密算法,可以将私钥推导出公钥。且通过私钥加密,公钥可以验证签名是否正确,而且几乎无法通过公钥推算出私钥的算法,这里亦使用了纯JS来实现算法([ecdsa-secp256k1仓库地址](https://github.com/zy445566/ecdsa-secp256k1))。
# 工作量证明
何为工作量证明,比如在很多公司喜欢粗暴地将工作时间和工作量挂钩,催生了996文化,狗性文化。这属于管理者的一种懒政,或者说通过简单的或低成本方式来验证员工的工作量,至少不用24小时盯着你看。那么在程序中,如果计算机是一个只会做运算的工人,如何设计一种算法来通过低成本的方式来计算计算机计算的工作量呢?
其实答案很简单,举例我需要你在0到1OOO亿中,使用摘要算法比如SHA-256找出开头是888888的摘要字符串的那个数字,那么你可能找了好几百万次才找到这个数字,这个数字计算出的摘要字符串开头是888888。但是我要校验就很简单,只需要将你给我的这个数字,我用相同的摘要算法进行运算得到摘要字符串之后,验证它的前6位是否是8就可以找到了。
工作量证明只是方法,存在问题就是如何在计算机算力高速发展或矿工大量罢工导致算力突降的基础上实现工作量的稳定动态增加或动态降低。这点其实很简单比如你计算6个8的摘要字符串的时间大于十分钟,则我把难度调成下个摘要只需要计算5位,如果在十分钟内完成,我把下个摘要增加到计算7位。
# 钱包和交易
在现实的理想的世界,你的钱包只有你一个人才能打开,并只有你想要买东西的时候,才会打开钱包为你想买的东西付账。那么在网络世界应该如何实现一个类似钱包的东西呢?
在剧前提要提到过ecdsa-secp256k1椭圆加密算法,这种算法可以使用私钥进行签名,且公钥可以验证签名,且公钥难以反推出私钥。那么我们是不是可以使用私钥作为我们的钱包的钥匙,当我们需要付钱的时候使用私钥进行签名,当别人需要确认交易的时候,只需要将公钥验证交易的签名是否通过,那么这个交易就是经过你授权且有效的。
那么私钥其实是做了一个取钱的钥匙,那么存钱的入口在哪里?ecdsa-secp256k1椭圆加密算法有一个重要的特点就是私钥可以推导出公钥,那么公钥是不是就能作为钱包地址即存钱的入口。只要你能通过私钥推导出这个公钥,就证明这个钱包是你的。
# 区块链
那么上面说了工作量证明和交易,那么工作量证明的意义体现在哪里?需要通过什么东西来承载这些交易数据。对!就是区块链。
什么是区块链?讲区块链前,我认为先讲单独的区块是什么可能更为合理,单独区块可以理解为使用算力的工作量为担保,来证明交易是否真实的证据。那么区块链就很好理解了,通过区块链把这些区块串联起来。
那么区块是如何串联的呢?在FitBlock是这样做的,比如第一个区块链通过工作量证明计算出了一个888888...666666即开头是888888结尾是666666的一个摘要字符串,假设它的高度是0,那么下一个区块则要计算开头是666666的区块,这个时候它的高度是1,以此类推那么下一个区块则需要再取上个区块摘要结尾的N位来计算下一个区块,则下个区块的高度继续加1。
那么根据上面的一点,你要推翻一个区块,则需要找到计算出和它一样开头的区块摘要,但是假设目前区块高度是999,而你要推翻第1个区块,那么你要从第一个区块计算到第1000个区块,只有你的区块高度大于别人的区块链才表面你有足够的证据推翻,但这个几乎是不可能的,因为每个区块都是根据当前的区块链上算力来推算出,即使你要推翻第高度是999的区块,都要使用当前区块链上51%以上的算力才能推翻上一块。只要所有正义者算力大于51%,坏人都无法得逞。
而区块链几乎是无法被篡改,那么你的交易只要接受被矿工接受,并打入区块,你的交易就几乎是无法被篡改的?那么问题来了矿工为什么要为你打包交易进入区块?
其实在一定高度区块链的区块链中,在某个矿工进行工作证明后出块是有出块奖励。并且你的交易都会有一定交易费给予矿工,保证了矿工的收入。
# P2P网络
那么在区块链中传播是一个十分重要的环节,要能即时发现节点并在周围节点启动时能够及时发现节点,包括在内外网络的节点打通问题,该如何处理.
我认为首先需要一个引导节点尤其是世界上节点较少的时候,引导节点可以使连入的节点快速发现其他节点,通过节点和节点之间的节点分享,快速建立节点网络。其次通过引导节点来引导内网节点来做节点中继,帮助内网也能快速连接到外网的其它节点。
其次需要保证某个节点只负责一定的范围,这样只要周边节点上线就可以以最快的速度发现节点,同时优先周边节点本身网络距离近,那么网络传输必然比远程节点更加迅速。减少网络的传播时间。
# 结语
第一次使用JS实现一个完整的区块链系统。也算是遇到了一些坑,在下次实现区块链系统时,不要以复杂的系统为考虑,提前把系统做的复杂。第二个坑是使用了GRPC,因为GRPC有Node.js模块是使用了C++模块,导致我要前后端使用一个核心库,要重新进行分离。第三过早的使用Web Components好处特别轻量,周边过于不完善,对于CSS弱没有UI库确实比较麻烦。在写这个系统的的时候,我认为最佳部分是使用了TS,在TS的加持下,确实很多东西方便了许多。其次使用lerna和git的submodule管理不同模块,管理和提交都相对清晰。
================================================
FILE: article/block/ipfs-base/README.md
================================================
# ipfs基本介绍
由于看到网上大部分IPFS的介绍都十分含糊不清,所以觉得IPFS急需要一个更强力的科普文来介绍IPFS。所以搬了官网的介绍来进行说明。
# 前置知识
用磁力链开过车的老司机都知道,只要在下载器里面输入磁力链链接,不管这个磁力链的文件在“某个老司机的电脑”里,下载器都能够嗅探并下载下来,同时你也会成为这个文件的“某个老司机的电脑”。
这是P2P(不是借钱跑路的P2P)网络传输的一种模式,即所有的机器都是对等关系,即是服务器又是客户端,且无主从之分,是一种非中心化的分布式网络。(或许这已经是趋势了)
当然IPFS也不仅仅就是这样。
# 今天互联网存在什么问题,IPFS能带来什么样的改变
## HTTP效率低且成本高

HTTP重复从一个中心节点下载文件,而不是同时从多个节点上获取文件。然而视频传输,通过P2P方法可以节省60%的带宽成本。
IPFS可以高效地分发大量数据。零重复意味着节省存储空间。
## 每天都会删除的历史

网页的平均寿命为100天。还记得GeoCities(最早一批提供个人主页服务的网站,后来关闭,导致大量用户资料丢失)吗?一旦中心节点不再存在,我们这个时代的主要媒介就会土崩瓦解,这还不够好。
IPFS保留了文件的每个版本,并使设置弹性网络以便镜像数据变得简单。
## 网络的中心化造成了垄断

互联网一直是人类历史上最伟大的均衡器之一,也是真正的创新加速器。但互联网垄断总是相对简单。
IPFS仍然忠实于开放式和扁平化网络的最初愿景,但提供了使该愿景成为现实的技术。
## 我们的应用程序过于依赖中心节点

中心节点在开发中,断线,自然灾害,间歇性连接。与星际网络系统(IPNS)相比,所有这些都是微不足道的。在我们使用的网络是20世纪,我们可以做得更好。
IPFS支持创建具有多种弹性的网络,无论是否具有Internet骨干网连接,都可实现持久可用性。
# IPFS的工作原理
1. 每个文件及其中的所有块都被赋予一个称为加密哈希的唯一指纹

2. 消除了网络上的重复文件。

3. 帮助每个网络节点仅存储它感兴趣的内容,以及一些索引信息,确定存储内容。

4. 当你查找文件时,你会通过唯一的哈希值在存储文件的节点上查到文件。

5. 每个文件都可以使用名为星际网络系统(IPNS)的分散命名系统,保证可通过人类可读的名称找到。

# 学习更多内容
* [下载一个ipfs桌面客户端(支持win,mac,linux)](https://github.com/zy445566/ipfs-desktop/releases)
* [了解IPFS的基础知识以及启动和运行中文指南](https://github.com/zy445566/ipfs-doc-zh/blob/master/README.md)
* [针对docker的ipfs服务](https://github.com/zy445566/ipfs-docker)
================================================
FILE: article/block/publish-coin/Coin.sol
================================================
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
interface IERC20 {
// token名字
function name() external view returns (string memory);
// token唯一标识,用于发放代币
function symbol() external view returns (string memory);
// token额度最小单位是小数点的后多少位,类似Ether的个和Wei或BTC的个和聪的关系
function decimals() external view returns (uint8);
// 交易事件
event Transfer(address indexed from, address indexed to, uint256 value);
// 赋予事件
event Approval(address indexed owner, address indexed spender, uint256 value);
// token总量查询
function totalSupply() external view returns (uint256);
// 某个账户token余额查询
function balanceOf(address account) external view returns (uint256);
// 从当前账户转账,结束后需要调用Transfer事件
function transfer(address to, uint256 amount) external returns (bool);
// 获取toekn授权量
function allowance(address owner, address spender) external view returns (uint256);
// 将自己的token授权给某个账户一定的量,结束后需要调用Approval事件,一般用于委托转帐
function approve(address spender, uint256 amount) external returns (bool);
// 传入指定从某个账户转账到另一个账户,一般用于委托转帐调用,由调用方消耗Gas
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// 完整合约代码,可以在 https://github.com/zy445566 查看
contract Coin is IERC20 {
// 这个是用来铸币用的,我要给我自己发币可以使用这个
address public minter;
// 币的名称
string private _name;
// 币的标识
string private _symbol;
// 币的精度
uint8 private _decimals;
// 币的总量
uint256 private _totalSupply;
// 这个map类型,用于地址映射代币数量
mapping (address => uint256) private _balances;
// 这个是map嵌套类型,用于映射某个账户和另一个账户是否存在授权转账,一般用于代转账
mapping(address => mapping(address => uint256)) private _allowances;
// 初始化方法
constructor(string memory nameValue, string memory symbolValue, uint8 decimalsValue) {
// 把合约创建者变成铸币人
minter = msg.sender;
// 确定币的名字
_name = nameValue;
// 确定币的标识,有点类似于股票发行代码
_symbol = symbolValue;
// 确定最小单位与个的精度
_decimals = decimalsValue;
// 初始供应链为0
_totalSupply = 0;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return _decimals;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
// 铸币方法
function mint(address receiver, uint256 amount) public {
require(msg.sender == minter, "ERC20:You aren't the owner");
_balances[receiver] += amount;
}
// 交易方法
function transfer(address to, uint256 amount) public virtual override returns (bool) {
// 校验钱是否足够
require(amount <= _balances[msg.sender], "ERC20:Insufficient balance.");
require(amount > 0, "ERC20:Amount has to be greater than 0.");
_balances[msg.sender] -= amount;
_balances[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
require(amount <= _balances[msg.sender], "ERC20:Insufficient balance to approve.");
require(amount > 0, "ERC20:Amount has to be greater than 0.");
_balances[msg.sender] -= amount;
_allowances[msg.sender][spender]+= amount;
emit Approval(msg.sender, spender, _allowances[msg.sender][spender]);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
require(amount <= _allowances[from][to], "ERC20:Insufficient allowance.");
_allowances[from][to] -= amount;
emit Approval(from, to, _allowances[from][to]);
_balances[to] += amount;
emit Transfer(from, to, amount);
return true;
}
}
================================================
FILE: article/block/publish-coin/README.md
================================================
# 在新的一年,我做了一个违背链圈的决定,把发币的教程手把手教给大家
首先我这篇文章真不是标题党,因为区块链的课程都是相对比较贵的,包含如何发币的课程很多是几万人民币打底,加上区块链市场人才的极度缺失,想要利用区块链技术捞钱的老板只能砸重金招募人才,甚至在几年前,你只要会发币到uni或sushi放上去交易,就能轻松赚上上百万刀和上千万人民币。所以免费给小白做的发币教程无疑是背叛了区块链技术圈。
# 0x1 工具准备和安装
该教程仅适用mac电脑或linux系统。如果是win系统,请自行转换相对路径进行。
在工具使用方面,我觉得最好用的应该属于hardhat,为什么呢?因为它涵盖了开发,测试,发行,还带有独自的虚拟eth私有网络,简直可以说是开发上线一条龙服务。这个体验无疑是非常棒,丝滑且顺畅的。
这个工具是使用node.js进行开发的,所以前置条件是我们需要一个node.js,这个对于前端的同学来说简直是利好,毕竟每位前端同学几乎都安装了node.js。但就目前来说JS在区块链方向上的发展可以说处于超越任何语言的高速通道,所以只要你想要进入区块链方向,那JS可以说是必学的一门语言。
node.js的安装,我就不多说了,有需要的直接[[点此]](https://nodejs.org/en/download/)进入官网下载,熟悉的可以自行使用nvm安装从而进行node.js的版本管理。
接下来新建文件夹来初始化项目,并安装hardhat依赖,命令如下:
```sh
# 创建项目文件夹
mkdir myProject
# 进入项目文件夹
cd myProject
# 初始化node.js工程,此处可以疯狂按回车
npm init
# 安装hardhat依赖,此处稍作等待安装完毕
npm install --save-dev hardhat
# 接下来初始化hardhat项目,这里我比较喜欢创建ts项目提示好些,其他直接回车即可
npx hardhat
```
这几步下来项目就基本初始化好了,我们可以进入下一步合约的开发和编写了
# 0x2 合约的开发和编写
好了项目初始化好了,我们可以看下相应的结构。
```sh
.
├── README.md
├── contracts
│ └── Lock.sol
├── hardhat.config.ts
├── package-lock.json
├── package.json
├── scripts
│ └── deploy.ts
├── test
│ └── Lock.ts
└── tsconfig.json
```
我们可以看到在合约文件夹(`contracts`)有一个自带的合约Lock.sol,这个是hardhat给我们做的一个小demo,我们可以看到Lock.sol的代码其实是使用solidity语言写的合约,这个语言是以太坊推出的DSL,用于运行在以太坊虚拟机中。
```sh
# 那我们可以自己建立一个合约,姑且叫Coin.sol吧
touch ./contracts/Coin.sol
```
OK,那如果我们要发币,我们需要实现ERC20协议,什么是ERC20协议呢?ERC是“EthereumRequest for Comment”缩写,也就是“以太坊征求意见协议”的缩写,而ERC20协议可以说是所有ERC协议中最广为人知的协议,大家使用ETH进行跨币转账,几乎都是使用ERC20,但这个也不是没有缺点,就是费Gas。所以我们也要基于ERC20协议开发我们的代币。
有兴趣可以查看ERC20协议更多介绍[点此查看ERC20协议标准详细介绍](https://eips.ethereum.org/EIPS/eip-20)
这个时候,你要要实现一个协议,会不会很难,感觉工程很庞大,其实你多虑了,ERC20协议只有几个简单的方法需要实现,然后每个方法的作用和目的,我都进行了标注。如下
```ts
interface IERC20 {
// token名字
function name() external view returns (string memory);
// token唯一标识,用于发放代币
function symbol() external view returns (string memory);
// token额度最小单位是小数点的后多少位,类似Ether的个和Wei或BTC的个和聪的关系
function decimals() external view returns (uint8);
// 交易事件
event Transfer(address indexed from, address indexed to, uint256 value);
// 赋予事件
event Approval(address indexed owner, address indexed spender, uint256 value);
// token总量查询
function totalSupply() external view returns (uint256);
// 某个账户token余额查询
function balanceOf(address account) external view returns (uint256);
// 从当前账户转账,结束后需要调用Transfer事件
function transfer(address to, uint256 amount) external returns (bool);
// 获取toekn授权量
function allowance(address owner, address spender) external view returns (uint256);
// 将自己的token授权给某个账户一定的量,结束后需要调用Approval事件
function approve(address spender, uint256 amount) external returns (bool);
// 传入指定从某个账户转账到另一个账户
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
```
其实主要的方法就是获取标识和交易以及授权交易(代扣款)的实现,接下来我将对这些方法进行一一实现。
首先我们需要进行一下我们刚刚协议接口的继承,这里的继承不是我们一般语言用的extend关键字而是is,通过继承接口我们就需要对接口的内容做一一实现了,像简单的get方法,我们可以定义好,和一些初始化的设置,可以看我下面这个实现。这些我认为是可以很简单看懂的,大家可以先看一下。
```ts
contract Coin is IERC20 {
// 这个是用来铸币用的,我要给我自己发币可以使用这个
address public minter;
// 币的名称
string private _name;
// 币的标识
string private _symbol;
// 币的精度
uint8 private _decimals;
// 币的总量
uint256 private _totalSupply;
// 这个map类型,用于地址映射代币数量
mapping (address => uint256) private _balances;
// 这个是map嵌套类型,用于映射某个账户和另一个账户是否存在授权转账,一般用于代转账
mapping(address => mapping(address => uint256)) private _allowances;
// 初始化方法
constructor(string memory nameValue, string memory symbolValue, uint8 decimalsValue) {
// 把合约创建者变成铸币人
minter = msg.sender;
// 确定币的名字
_name = nameValue;
// 确定币的标识,有点类似于股票发行代码
_symbol = symbolValue;
// 确定最小单位与个的精度
_decimals = decimalsValue;
// 初始供应链为0
_totalSupply = 0;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return _decimals;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
// other code ...
}
```
* 其中virtual override关键字代表覆盖接口重写
* string为字符串类型,uint8和uint256为不同位的整数类型
* view关键字代表仅查看,因为非查看的操作是要写入区块的,那么会产生费用。
接下来我们重点说一下《铸币方法》和《交易方法》,完整代码可以到[我的博客](https://github.com/zy445566)的该篇文章的文件夹中看到。
其实代码部分远比我们想象的要简单,可能很多人觉得写这个需要算法,其实ETH已经帮我们实现了整个区块链的功能,我以前也完整实现过自己的区块,这里面需要包含大量的算法,比较有趣的比如椭圆加密算法也有比较有意思的思路比如POW。但我们这次不需要,我们只需要在ETH上开发应用就能简单发币。
接下来的几行代码所以,实现币功能的简约性,会超乎你的想象,如下
```ts
contract Coin is IERC20 {
// 铸币方法,此方法非ERC20协议,纯粹是是自己加的方法,目的是给自己打钱用的
function mint(address receiver, uint256 amount) public {
require(msg.sender == minter, "ERC20:You aren't the owner");
_balances[receiver] += amount;
}
// other code ...
}
```
好比铸币方法你只需要给自己加钱就可以了,加完钱后消耗一定的交易费(Gas)就可以,写入ETH区块。
* 其中_balances是我在类中定义好的变量
* msg主要是包含调用者的一些信息
而交易方法,也是非常简单,只需要让执行transfer方法的人,来进行扣钱,然后让他想要转账的人加钱即可。然后提交一个交易事件,把交易事件给需要监听的人就好了。如下:
```ts
contract Coin is IERC20 {
// 交易方法
function transfer(address to, uint256 amount) public virtual override returns (bool) {
// 校验钱是否足够
require(amount <= _balances[msg.sender], "ERC20:Insufficient balance.");
require(amount > 0, "ERC20:Amount has to be greater than 0.");
_balances[msg.sender] -= amount;
_balances[to] += amount;
// 提交交易事件
emit Transfer(msg.sender, to, amount);
return true;
}
// other code ...
}
```
可能有人会说真就这么简单么?那可不么,由于ETH屏蔽了底层实现,几行代码就完成了一个交易功能,但区块链的难点永远都不会是功能的实现而是黑客与攻防和底层加密算法,发现一个著名区块链或合约或加密算法的BUG,等价于你找到一个提款机,当然不是本章的重点,否则就太长了,哈哈哈。
OK,合约代码部分写的差不多了,我们可以着手开始发布我们的合约,并开始发币了。[手动狗头]。
# 0x3 部署合约,接入metamask钱包。
首先我们可以使用hardhat启动我们的私域ETH网络,如下命令:
```sh
# 用hardhat启动私域ETH网络
npx hardhat node
```
启动完成后,里面会有打印RPC的URL地址和一些私域ETH网络准备好的ETH的账户地址和私钥。以及记录下RPC的URL地址。
基本完成了,这时候我们打开metamask,这个是一款区块链钱包,由以太坊组织主导开发的,当然其他钱包大多也都支持ERC20协议,比如Trust Wallet等等。了解详情可以进入[metamask官网](https://metamask.io/),安装完成打开即可,我这边就不过多介绍了,币圈都比较熟悉,哈哈哈。
安装metamask完成后,我们设置一下metamask界面添加私域ETH网络。如下图

点击保存完成配置
图片解释:
* 网络名称按自己喜好填写即可
* 新的RPC URL则填入网络打印的RPC的URL地址
* 链 ID则填入hardhat默认的31337。链 ID的作用是啥呢?这个是ETH当初一个多链双花的问题解决的提案
* 货币符号填入ETH即可
然后我们需要在我们的scripts文件夹中,建立一个scripts/deployCoin.ts文件来做部署脚本。主要作用是在里面写部署代码,然后来进行部署。主要就是初始化合约,代码如下:
```ts
import { ethers } from "hardhat";
async function main() {
// 这里根据我们合约文件夹[contracts]需要部署的合约名称的的名称填入
const Coin = await ethers.getContractFactory("Coin");
// 给我的币取名zy445566
const nameValue = "zy445566";
// 给我的币的标识zyCoin
const symbolValue = "zyCoin";
// 设置币的精度
const decimalsValue = 18;
// 初始化部署
const coin = await Coin.deploy(nameValue, symbolValue, decimalsValue);
// 等待部署完成
await coin.deployed();
// 获取铸币人
const minter = await coin.minter();
// 给自己打1个亿自己的代币,因为小数点是18,所以要乘以10的18次方
// 数字后面带n主要是JS的bigint语法
await coin.mint(minter, 1n*10n**8n*10n**18n)
// 打印一下关键信息
console.log(`Coin deployed!nameValue:${nameValue},deployed to ${coin.address},minter is ${minter}`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
```
我们新开一个命令行窗口,直接用hardhat执行脚本的部署即可,运行以下命令。
```sh
# 用hardhat执行对应的脚步部署文件,部署到我们的私域ETH网络,如果发布到主网需要配置主网信息
npx hardhat run scripts/deployCoin.ts --network localhost
```
执行完命令后,你应该会输出Coin deployed!后面跟着关键信息nameValue,symbolValue,deployedAdress和minterAdress,这些后续都要使用,在后续跟大家说。
然后我们在私域ETH网络准备好的ETH的账户中找到minterAdress的私钥,并导入到metamask中,接下来在metamask点击添加资产按钮。
* 代币合约地址为部署时输出的deployedAdress地址
* 代币符号为输出的symbolValue
* 代币小数为decimalsValue
如下图:

添加完成,则看到你的私域ETH网络出现了你的代币和数量
如下图:

接下来你可以尝试给自己的另一个账号转账和用自己的代币交易了,如图。

注意接收到代币的一方也是要注意添加资产的哦!
# 结语
至此购买一定的ETH作为发行到主网代币的费用就可以发行你的代币并上链交易了,同时编写好你的故事,上coinlist或swap平台就可以等别人花钱给你买酒了。祝您早日暴富,哈哈哈
================================================
FILE: article/design-pattern/README.md
================================================
# javascript的设计模式
* [javascript的设计模式(20191118)](https://github.com/zy445566/design-pattern-in-javascript/)
* [写《javascript的设计模式》的一些总结(20191126)](../design-pattern/design-pattern-knowledge/README.md)
================================================
FILE: article/design-pattern/design-pattern-knowledge/README.md
================================================
# 写《javascript的设计模式》的一些总结
最近复刻了一个[《javascript的设计模式》](https://github.com/zy445566/design-pattern-in-javascript/)。也再一次温习了js的一些看似不怎么用的知识点,但是在设计模式中又是非常重要的。对于这种容易遗忘的细节但却重要的东西,我觉得来记录这些知识点,以便需要的时候可以看看。
# 0x1 如何通过基类克隆一个子类对象
首先我们知道JS是基于设计模式中的原型模式设计原型链,那么类一定是有共通的原型在里面。那么在子类实例化之后,在基类的this也就变成了子类的this,那么只需要通过新建一个对象,并把当前子类的prototype即实例化类的__proto__重现给予到新对象中,由于this.__proto__是子类的prototype,那么绑定新对象调用子类的prototype的constructor即可实现,这也是new的过程。
但是在es6中有一些变更就是如果使用了class关键字,是不可以被call调用的,但是还是可以通过ES6推出的Reflect.construct来达到相同效果,代码如下,下面也有详细说明。
```js
// 这是基类
class Shape {
// 代码省略...
clone() {
/**
* 如果子类要改成class形式,这个方法要改写成下面形式
* 因为主要是通过JS原型链帮助理解原型模式,所以子类不使用class形式
* class和function构造函数的区别是class的构造函数增加了只能作为构造函数使用的校验,比如new
* return Reflect.construct(
* this.__proto__.constructor,
* [],
* this.__proto__.constructor
* )
*/
let clone = {};
// 注意如果此类被继承,this会变成子类的方法
// 同时这里使用的是原型的指针,所以比直接创建对象性能损耗更低
clone.__proto__ = this.__proto__;
this.__proto__.constructor.call(clone);
return clone;
}
}
// 这是子类
function Rectangle() {
this.type = "Rectangle";
}
Rectangle.prototype.__proto__ = new Shape();
Rectangle.prototype.draw = function() {
console.log("I'm a rectangle")
}
```
# 0x2 如何实现一个抽象方法
设计模式中有很多抽象方法,但是抽象方法是不能被初始化,只能被继承,那么抽象类要如何实现呢?
其实有两种方法,一种是通过判断this是否instanceof这个基类,二是用ES6的方法使用new.target,如下:
```js
class AbstractLogger {
constructor() {
if(new.target == AbstractLogger) {
throw new Error('this class must be extends.')
}
}
// 代码省略...
}
```
# 0x3 如何实现私有变量
实现私有变量有很多方法,比如Symbol,但Symbol的实现需要通过作用域隔离,其次当访问私有属性的关键字只能返回undefined,没有错误信息抛出,这是一种非常不好的设计或实践.
那么私有属性还没有推出,如何来更好的实现呢?可以通过数据定义的方式来做这件事情,即defineProperty。
那么问题又来了,因为私有属性只允许自己调用,子类不能调用,那么如何保证是自己而不是子类或者其它类型呢?那么可以根据当前this的__proto__来判断,如果__proto__引用等于自己的prototype则为自己。因为如果是子类继承,那么this的__proto__等于继承者的prototype。那么根据这一点,我们可以这样做,如下:
```js
class Meal {
constructor () {
const items = [];
/**
* 为什么不用Proxy而使用defineProperty
* 因为Proxy虽然实现和defineProperty类似的功能
* 但是在这个场景下,语意上是定义属性,而不是需要代理
*/
Reflect.defineProperty(this, 'items', {
get:()=>{
if(this.__proto__ != Meal.prototype) {
throw new Error('items is private!');
}
return items;
}
})
}
// 省略代码...
}
```
# 0x4 如何实现类的终态方法
在设计模式中,很多方法要实现终态化,即基类的方法不能被子类覆盖。
如何做到不被子类方法覆盖父类,貌似在JS中是个难题,但真的是难题吗?
并不是,因为当子类实例化的时候需要调用父类的构造函数,但此时父类的构造函数的this就是子类的方法,而JS对象构造又是基于原型的,那么如果子类自己实现了方法,那么子类实现的方法必然不等于父类原型中的方法,通过这个方法来实现父类方法的终态化。如下:
```js
class Game {
constructor() {
if(this.play!= Game.prototype.play) {
throw new Error("play mothed is final,can't be modify!");
}
}
// 代码省略...
play(){
// 代码省略...
}
}
```
# 结语
通过这些有意思的方法实现通过基类克隆一个子类对象,不可被初始化,私有化变量,终态方法的实现。即感叹JS的灵活性,但又对各种行为保有余地,这非常棒。同时在写[《javascript的设计模式》](https://github.com/zy445566/design-pattern-in-javascript/)的时候,发现JS本身就是一个设计模式的教科书,是很值得我们学习的。
以上例子出自于[《javascript的设计模式》](https://github.com/zy445566/design-pattern-in-javascript/)。
================================================
FILE: article/docker/README.md
================================================
# docker
* [迫于开始使用windows,但docker还是不能少(20190808)](../docker/wsl-remote-docker/README.md)
================================================
FILE: article/docker/wsl-remote-docker/README.md
================================================
# 迫于开始使用windows,但docker还是不能少
由于本人更换公司且公司统一使用windows所以开始使用windows电脑,但unix命令行用习惯了,所以再让我重新用cmd还是powerShell想想还是不太舒服,所以转战wsl(win10如果应用商店没有卸载可以点击这个连接安装[https://aka.ms/wslstore](https://aka.ms/wslstore),同时vscode重新选择默认Shell可以直接在vscode上使用哦),但是一番折腾后居然发现wsl的由于是虚拟环境docker的服务端居然启动不了,但wsl用docker的客户端还是很不错的。所以想想还是在宿主机装个服务端勉勉强强还能在wsl用用,随开始安装。
# win10家庭版给了我一个暴击
docker在windows有两个常用版本一个是docker desktop用的是win自带的hyper-V虚拟的环境,另一个是给没有自带hyper-V的系统用toolbox用virtualbox虚拟的环境。
一开始抱着能用就行的心态用了toolbox版本,但缺点大的我几乎无法忍受,且不说用virtualbox虚拟环境给机器带来的强大负担,更让人无法忍受的是docker命令居然只能在cmd上使用,在powershell都不能用,否则会报这个命令是cmd命令,非cmd不能使用。
忍不了,但win10家庭版没有自带hyper-V不能支持docker desktop,根据教程用命令开启了家庭版的hyper-V,并且修改注册表来改成专业版,但问题来了,只能欺骗低版本的docker desktop不能欺骗高版本的docker desktop,最重要的是低版本的一直会提示升级,真担心我手贱点一下就用不了,最终作为强迫症患者,我选择放弃,送他进我的windows垃圾桶。
# 瞄准了开发机
突然想到喵的,开发机是linux的,利用上面的docker服务不就好了。所以连上开发机。修改服务文件。
```sh
vim /lib/systemd/system/docker.service
```
找到ExecStart这个配置在后面加一个 -H tcp://0.0.0.0:2375
```conf
# 比如我的是 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
# 然后修改成下面这样,可以既监听本地也可以监听外网端口
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H fd:// --containerd=/run/containerd/containerd.sock
```
然后重启下服务
```sh
systemctl daemon-reload
systemctl restart docker
```
# 一顿操作猛如虎,看看我的电脑能不能连上
我内网的那台开发机的IP是192.168.1.22,所以感觉试试
```sh
docker -H 192.168.1.22:2375 version # 这里替换成自己的docker服务的IP
```
出现Server的信息就妥了。但又有一个问题就是不可能一直使用 -H 192.168.1.22:2375 太麻烦了。所以用命令设个变量就妥了。
```sh
export DOCKER_HOST=tcp://192.168.1.22:2375 # 如果要开机启动可以加入 ~/.bashrc后source
```
# 使用体验
使用之后爽了,和linux上使用是一毛一样了。缺点就是你构建了docker的image还是启动了容器都不是在本地启动,而是在开发机上启动,不过对我来说也好,毕竟节约资源了。
================================================
FILE: article/front/README.md
================================================
# 前端
* [全栈(gan)工程师第一步:搭建一个全栈(gan)框架(20190824)](../front/make-full-stack-framework/README.md)
* [为什么我们还要使用前端框架来构建页面?(20191023)](../front/whyneedframework/README.md)
================================================
FILE: article/front/make-full-stack-framework/README.md
================================================
# 全栈(gan)工程师第一步:搭建一个全栈(gan)框架
作为一个全栈(gan)工程师是不是很苦恼,在一个全栈(gan)项目中被主流前后端项目所迫害所以只能多仓库开发,甚至于启动前端一个命令,启动后端一个命令。
现在发挥node.js全栈(gan)优势的机会来了,拒绝两个仓库,拒绝两个命令,转战真全栈(gan)模式!
# 0x01:将前后端和中间层合并到一个仓库
合并完目录如下:(当前demo代码:[full-stack-demo地址](https://github.com/zy445566/myBlog/tree/master/20190824fullstack/20190824make-full-stack-framework/full-stack-demo))
```sh
full-stack-demo
├── app
│ ├── back # 这里是后端文件夹
│ │ ├── router.js # 后端路由配置
│ │ ├── src
│ │ │ └── controller # 由于这是教程所以只保留了控制器
│ │ │ └── HelloController.js
│ │ └── static.js # 后端静态文件配置
│ ├── config.js
│ ├── front # 前端文件夹
│ │ ├── run.js # 运行前端的node文件
│ │ ├── src
│ │ │ └── index.jsx # 由于这是教程所以只保留了前端路口文件
│ │ ├── static # 静态文件文件夹
│ │ │ ├── index.html # 前端首页
│ │ │ └── webpack.bundle.js # webpack生成的js文件
│ │ └── webpack.config.js # webpack配置文件
│ └── middleware # 中间件文件夹
│ ├── application-type.js # 返回头的类型
│ ├── router.js # 路由中间件
│ └── static.js # 静态文件中间件
├── main.js # 项目入口文件
├── Dockerfile # docker容器文件
└── package.json
```
也就是说我们用类似于monorepo的模式将前后端都作为了当前app的子项目文件。我们用入口文件main.js启动整个项目。在简单提一下在dev模式关于热更新,我们使用nodemon来实现后端热更,而前端热更则使用wepack的watch来实现。为了防止前后端热更冲突,我们可以使用package.json来配置nodemon忽略前端目录实现。
```json
"nodemonConfig": {
"ignore": [
"app/front/*"
]
}
```
# 0x02:启动和入口文件
在我们开发模式中,我们常常使用nodemon来启动服务
```sh
npx nodemon main.js
```
通过执行main.js达到执行前后端的效果
main.js文件如下:
```js
const Koa = require('koa');
const path = require('path');
// app的配置文件
const config = require('./app/config');
// 路由中间件
const router = require('./app/middleware/router');
// 静态文件中间件
const static = require('./app/middleware/static');
//实例化koa
const app = new Koa();
// 这里相当于后端做静态文件服务而不使用webpack-dev-server
app.use(static(require('./app/back/static')));
// 使后端路由配置生效
app.use(router(require('./app/back/router')));
// 读取app配置绑定ip并监听端口
app.listen(config.port, config.hostname);
// 前端以子进程的方式启动,配置好启动路径
require('child_process').fork('run.js',process.argv,{cwd:path.join(__dirname,'app','front')});
```
这里相当于启动后端服务后,再用子进程来启动前端服务。那么我们来谈谈前端的启动。
# 0x03:前端的启动
前端则是使用webpack的api模式来启动,而非是传统的CLI的方法来启动项目,通过区分启动参数来进行启动模式的判定,比如在dev模式中则使用watch的方式,来查看变更,而在正式环境只通过run来编译一次生成静态文件。
```js
const config = require('../config');
const webpack = require('webpack');
// 这里直接引用webpack.config.js而不是通过CLI来读取
const compiler = webpack(require('./webpack.config.js'));
// 这里判断参数是否只编译一次还是走dev的watch方式
let compilerOnce = false
for(let arg of process.argv) {
if(arg=='--stable') {
compilerOnce = true;
}
}
// 运行webpack的回调
function runBack(err, stats) {
if (err) {
console.error(err);
return;
}
console.log(stats.toString({
errors: true,
warnings: true,
modules: false,
chunks: false,
colors: true
}));
console.log(`link: http://${config.hostname}:${config.port}/`);
}
// 判断是只编译一次还是热更
if(compilerOnce) {
compiler.run(runBack)
} else {
/**
* 这里和webpack-dev-server不一样
* webpack-dev-server默认watch到内存中
* 而这种方式则会真正生成文件
*/
compiler.watch({
aggregateTimeout: 300,
poll: undefined
},runBack);
}
```
值得一提的是后端如何和webpack联动而不用webpack-dev-server实现前端静态化。首先我们看一下webpack.config.js文件:
```js
const path = require('path');
module.exports = {
mode:'development',
entry: './src/index.jsx',
output: {
// 注意这里,其实后端的配置文件的静态服务也在这里打开的
path: path.join(__dirname,'static'),
filename: 'webpack.bundle.js'
},
resolve: {
alias: {
'@': path.join(__dirname,'src')
}
},
module: {
rules: [
{
test: /\.m?jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
}
}
}
]
}
};
```
在后端的配置full-stack-demo/app/back/static.js中:
```js
const path = require('path');
const config = require('../config');
module.exports = [
{
urlPrefix:'/',
staticPath:path.join(config.appDirname,'front','static'),
defaultList:['index.html']
}
]
```
配置了静态化full-stack-demo/app/front/static/目录,同时指定默认访问了index.html.而index.html则是引用了编译生成的webpack.bundle.js,这是前端环境正式生效。(画外音:顺带推荐个国际化的模块[my-i18n](https://www.npmjs.com/package/my-i18n)还不错)
index.html代码如下:
```html
<!DOCTYPE HTML>
<html>
<head>
<title>全栈(gan)demo</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/webpack.bundle.js"></script>
</body>
</html>
```
# 总结
自此实现了一行命令,从一个入口文件,同时执行前端和后端项目。能快速启动和开发一些简单的全栈(gan)场景和一些小型项目,但目前较大的前后端几乎都是采用微服务化的多APP模式,将多APP模式和这种全栈(gan)框架结合,目前还是有很多事情需要考虑的。就酱!
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/.dockerignore
================================================
# Dependency directories
node_modules/
# dotenv environment variables file
env.js
# project
webpack.bundle.js
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
env.js
# next.js build output
.next
# project
webpack.bundle.js
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/Dockerfile
================================================
FROM centos:latest
# 安装依赖
RUN yum -y update
RUN yum -y install wget
# 安装node环境
ENV NODE_VERSION v12.8.0
RUN mkdir -p /node/$NODE_VERSION
RUN wget https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-linux-x64.tar.gz
RUN tar xzf node-$NODE_VERSION-linux-x64.tar.gz -C /node/
ENV PATH /node/node-$NODE_VERSION-linux-x64/bin:$PATH
WORKDIR /myApp
# 复制文件(已使用.dockerignore)
COPY . /myApp
# 安装项目依赖
RUN npm install --registry=https://registry.npm.taobao.org
# 暴露端口
EXPOSE 3000
# docker入口文件
CMD ["npm","run", "stable"]
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/README.md
================================================
# RUN
```sh
cd 20190824fullstack/20190824make-full-stack-framework/full-stack-demo
npm install
npm run dev
```
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/back/router.js
================================================
const HelloController = require('./src/controller/HelloController');
module.exports = {
prefixPath:'/api',
routerMap:new Map([
['hello',HelloController],
])
}
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/back/src/controller/HelloController.js
================================================
class HelloController {
async world(ctx) {
return 'World';
}
}
module.exports = new HelloController();
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/back/static.js
================================================
const path = require('path');
const config = require('../config');
module.exports = [
{
urlPrefix:'/',
staticPath:path.join(config.appDirname,'front','static'),
defaultList:['index.html']
}
]
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/config.js
================================================
module.exports = {
hostname:'127.0.0.1',
port:3000,
appDirname:__dirname
}
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/front/run.js
================================================
const config = require('../config');
const webpack = require('webpack');
const compiler = webpack(require('./webpack.config.js'));
let compilerOnce = false
for(let arg of process.argv) {
if(arg=='--stable') {
compilerOnce = true;
}
}
function runBack(err, stats) {
if (err) {
console.error(err);
return;
}
console.log(stats.toString({
errors: true,
warnings: true,
modules: false,
chunks: false,
colors: true
}));
console.log(`link: http://${config.hostname}:${config.port}/`);
}
if(compilerOnce) {
compiler.run(runBack)
} else {
compiler.watch({
aggregateTimeout: 300,
poll: undefined
},runBack);
}
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/front/src/index.jsx
================================================
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {serverResp:'NO RESP'};
fetch('/api/hello/world').then((resp)=>{
if (!resp.ok) {return;}
return resp.json()
}).then((res)=>{
this.setState({
serverResp:res.data
})
})
}
render() {
return (
<div>
Hello {this.state.serverResp}
</div>
);
}
}
const domContainer = document.querySelector('#root');
ReactDOM.render(<App />, domContainer);
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/front/static/index.html
================================================
<!DOCTYPE HTML>
<html>
<head>
<title>全栈(gan)demo</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/webpack.bundle.js"></script>
</body>
</html>
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/front/webpack.config.js
================================================
const path = require('path');
module.exports = {
mode:'development',
entry: './src/index.jsx',
output: {
path: path.join(__dirname,'static'),
filename: 'webpack.bundle.js'
},
resolve: {
alias: {
'@': path.join(__dirname,'src')
}
},
module: {
rules: [
{
test: /\.m?jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
}
}
}
]
}
};
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/middleware/application-type.js
================================================
module.exports = {
".*": "application/octet-stream",
".pdf": "application/pdf",
".ai": "application/postscript",
".xml": "text/xml",
".js": "application/x-javascript",
".edi": "application/EDIFACT",
".json": "application/json",
".ogg": "application/ogg",
".rdf": "text/xml",
".woff": "application/font-woff",
".xhtml": "text/html",
".dtd": "text/xml",
".zip": "application/zip",
".gzip": "application/gzip",
".xls": "application/vnd.ms-excel",
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".001": "application/x-001",
".301": "application/x-301",
".906": "application/x-906",
".a11": "application/x-a11",
".awf": "application/vnd.adobe.workflow",
".bmp": "application/x-bmp",
".c4t": "application/x-c4t",
".cal": "application/x-cals",
".cdf": "application/x-netcdf",
".cel": "application/x-cel",
".cg4": "application/x-g4",
".cit": "application/x-cit",
".bot": "application/x-bot",
".c90": "application/x-c90",
".cat": "application/vnd.ms-pki.seccat",
".cdr": "application/x-cdr",
".cer": "application/x-x509-ca-cert",
".cgm": "application/x-cgm",
".cmx": "application/x-cmx",
".crl": "application/pkix-crl",
".csi": "application/x-csi",
".cut": "application/x-cut",
".dbm": "application/x-dbm",
".cmp": "application/x-cmp",
".cot": "application/x-cot",
".crt": "application/x-x509-ca-cert",
".dbf": "application/x-dbf",
".dbx": "application/x-dbx",
".dcx": "application/x-dcx",
".dgn": "application/x-dgn",
".dll": "application/x-msdownload",
".dot": "application/msword",
".der": "application/x-x509-ca-cert",
".dib": "application/x-dib",
".doc": "application/msword",
".drw": "application/x-drw",
".dwf": "Model/vnd.dwf",
".dxb": "application/x-dxb",
".edn": "application/vnd.adobe.edn",
".dwg": "application/x-dwg",
".dxf": "application/x-dxf",
".emf": "application/x-emf",
".epi": "application/x-epi",
".eps": "application/x-ps",
".exe": "application/x-msdownload",
".fdf": "application/vnd.fdf",
".etd": "application/x-ebx",
".fif": "application/fractals",
".frm": "application/x-frm",
".gbr": "application/x-gbr",
".g4": "application/x-g4",
".gl2": "application/x-gl2",
".hgl": "application/x-hgl",
".hpg": "application/x-hpgl",
".hqx": "application/mac-binhex40",
".hta": "application/hta",
".gp4": "application/x-gp4",
".hmr": "application/x-hmr",
".hpl": "application/x-hpl",
".hrf": "application/x-hrf",
".icb": "application/x-icb",
".ico": "image/x-icon",
".ig4": "application/x-g4",
".iii": "application/x-iphone",
".ins": "application/x-internet-signup",
".iff": "application/x-iff",
".igs": "application/x-igs",
".img": "application/x-img",
".isp": "application/x-internet-signup",
".jpe": "image/jpeg",
".jpg": "image/jpeg",
".lar": "application/x-laplayer-reg",
".latex": "application/x-latex",
".lbm": "application/x-lbm",
".ls": "application/x-javascript",
".ltr": "application/x-ltr",
".man": "application/x-troff-man",
".mdb": "application/x-mdb",
".mac": "application/x-mac",
".mfp": "application/x-shockwave-flash",
".mi": "application/x-mi",
".mil": "application/x-mil",
".mocha": "application/x-javascript",
".mpd": "application/vnd.ms-project",
".mpp": "application/vnd.ms-project",
".mpt": "application/vnd.ms-project",
".mpw": "application/vnd.ms-project",
".mpx": "application/vnd.ms-project",
".mxp": "application/x-mmxp",
".nrf": "application/x-nrf",
".out": "application/x-out",
".p12": "application/x-pkcs12",
".p7c": "application/pkcs7-mime",
".p7r": "application/x-pkcs7-certreqresp",
".pc5": "application/x-pc5",
".pcl": "application/x-pcl",
".pdx": "application/vnd.adobe.pdx",
".pgl": "application/x-pgl",
".pko": "application/vnd.ms-pki.pko",
".p10": "application/pkcs10",
".p7b": "application/x-pkcs7-certificates",
".p7m": "application/pkcs7-mime",
".p7s": "application/pkcs7-signature",
".pci": "application/x-pci",
".pcx": "application/x-pcx",
".pfx": "application/x-pkcs12",
".pic": "application/x-pic",
".pl": "application/x-perl",
".plt": "application/x-plt",
".png": "image/png",
".ppa": "application/vnd.ms-powerpoint",
".pps": "application/vnd.ms-powerpoint",
".ppt": "application/vnd.ms-powerpoint",
".prf": "application/pics-rules",
".prt": "application/x-prt",
".ps": "application/x-ps",
".pwz": "application/vnd.ms-powerpoint",
".ra": "audio/vnd.rn-realaudio",
".ras": "application/x-ras",
".pot": "application/vnd.ms-powerpoint",
".ppm": "application/x-ppm",
".pr": "application/x-pr",
".prn": "application/x-prn",
".ptn": "application/x-ptn",
".red": "application/x-red",
".rjs": "application/vnd.rn-realsystem-rjs",
".rlc": "application/x-rlc",
".rm": "application/vnd.rn-realmedia",
".rat": "application/rat-file",
".rec": "application/vnd.rn-recording",
".rgb": "application/x-rgb",
".rjt": "application/vnd.rn-realsystem-rjt",
".rle": "application/x-rle",
".rmf": "application/vnd.adobe.rmf",
".rmj": "application/vnd.rn-realsystem-rmj",
".rmp": "application/vnd.rn-rn_music_package",
".rmvb": "application/vnd.rn-realmedia-vbr",
".rnx": "application/vnd.rn-realplayer",
".rpm": "audio/x-pn-realaudio-plugin",
".rms": "application/vnd.rn-realmedia-secure",
".rmx": "application/vnd.rn-realsystem-rmx",
".rsml": "application/vnd.rn-rsml",
".rtf": "application/x-rtf",
".rv": "video/vnd.rn-realvideo",
".sat": "application/x-sat",
".sdw": "application/x-sdw",
".slb": "application/x-slb",
".sam": "application/x-sam",
".sdp": "application/sdp",
".sit": "application/x-stuffit",
".sld": "application/x-sld",
".smi": "application/smil",
".smk": "application/x-smk",
".smil": "application/smil",
".spc": "application/x-pkcs7-certificates",
".spl": "application/futuresplash",
".ssm": "application/streamingmedia",
".stl": "application/vnd.ms-pki.stl",
".sst": "application/vnd.ms-pki.certstore",
".tdf": "application/x-tdf",
".tga": "application/x-tga",
".sty": "application/x-sty",
".swf": "application/x-shockwave-flash",
".tg4": "application/x-tg4",
".tif": "image/tiff",
".vdx": "application/vnd.visio",
".vpg": "application/x-vpeg005",
".vsd": "application/vnd.visio",
".vst": "application/x-vst",
".vsw": "application/vnd.visio",
".vtx": "application/vnd.visio",
".torrent": "application/x-bittorrent",
".vda": "application/x-vda",
".vss": "application/vnd.visio",
".vsx": "application/vnd.visio",
".wb1": "application/x-wb1",
".wb3": "application/x-wb3",
".wiz": "application/msword",
".wk4": "application/x-wk4",
".wks": "application/x-wks",
".wb2": "application/x-wb2",
".wk3": "application/x-wk3",
".wkq": "application/x-wkq",
".wmf": "application/x-wmf",
".wmd": "application/x-ms-wmd",
".wp6": "application/x-wp6",
".wpg": "application/x-wpg",
".wq1": "application/x-wq1",
".wri": "application/x-wri",
".ws": "application/x-ws",
".wmz": "application/x-ms-wmz",
".wpd": "application/x-wpd",
".wpl": "application/vnd.ms-wpl",
".wr1": "application/x-wr1",
".wrk": "application/x-wrk",
".ws2": "application/x-ws",
".xdp": "application/vnd.adobe.xdp",
".xfd": "application/vnd.adobe.xfd",
".xfdf": "application/vnd.adobe.xfdf",
".xwd": "application/x-xwd",
".sis": "application/vnd.symbian.install",
".x_t": "application/x-x_t",
".apk": "application/vnd.android.package-archive",
".x_b": "application/x-x_b",
".sisx": "application/vnd.symbian.install",
".ipa": "application/vnd.iphone",
".xap": "application/x-silverlight-app",
".xlw": "application/x-xlw",
".xpl": "audio/scpls",
".anv": "application/x-anv",
".uin": "application/x-icq",
".323": "text/h323",
".biz": "text/xml",
".cml": "text/xml",
".asa": "text/asa",
".asp": "text/asp",
".css": "text/css",
".csv": "text/csv",
".dcd": "text/xml",
".ent": "text/xml",
".fo": "text/xml",
".htc": "text/x-component",
".html": "text/html",
".htx": "text/html",
".htm": "text/html",
".htt": "text/webviewhtml",
".jsp": "text/html",
".math": "text/xml",
".mml": "text/xml",
".mtx": "text/xml",
".plg": "text/html",
".rt": "text/vnd.rn-realtext",
".sol": "text/plain",
".spp": "text/xml",
".stm": "text/html",
".svg": "text/xml",
".tld": "text/xml",
".txt": "text/plain",
".uls": "text/iuls",
".vml": "text/xml",
".tsd": "text/xml",
".vcf": "text/x-vcard",
".vxml": "text/xml",
".wml": "text/vnd.wap.wml",
".wsdl": "text/xml",
".wsc": "text/scriptlet",
".xdr": "text/xml",
".xql": "text/xml",
".xsd": "text/xml",
".xslt": "text/xml",
".xq": "text/xml",
".xquery": "text/xml",
".xsl": "text/xml",
".odc": "text/x-ms-odc",
".r3t": "text/vnd.rn-realtext3d",
".sor": "text/plain",
".acp": "audio/x-mei-aac",
".aif": "audio/aiff",
".aiff": "audio/aiff",
".aifc": "audio/aiff",
".au": "audio/basic",
".la1": "audio/x-liquid-file",
".lavs": "audio/x-liquid-secure",
".lmsff": "audio/x-la-lms",
".m3u": "audio/mpegurl",
".midi": "audio/mid",
".mid": "audio/mid",
".mp2": "audio/mp2",
".mp3": "audio/mp3",
".mp4": "video/mpeg4",
".mnd": "audio/x-musicnet-download",
".mp1": "audio/mp1",
".mns": "audio/x-musicnet-stream",
".mpga": "audio/rn-mpeg",
".pls": "audio/scpls",
".ram": "audio/x-pn-realaudio",
".rmi": "audio/mid",
".rmm": "audio/x-pn-realaudio",
".snd": "audio/basic",
".wav": "audio/wav",
".wax": "audio/x-ms-wax",
".wma": "audio/x-ms-wma",
".asf": "video/x-ms-asf",
".asx": "video/x-ms-asf",
".avi": "video/avi",
".IVF": "video/x-ivf",
".m1v": "video/x-mpeg",
".m2v": "video/x-mpeg",
".m4e": "video/mpeg4",
".movie": "video/x-sgi-movie",
".mp2v": "video/mpeg",
".mpa": "video/x-mpg",
".mpe": "video/x-mpeg",
".mpg": "video/mpg",
".mpeg": "video/mpg",
".mps": "video/x-mpeg",
".mpv": "video/mpg",
".mpv2": "video/mpeg",
".wm": "video/x-ms-wm",
".wmv": "video/x-ms-wmv",
".wmx": "video/x-ms-wmx",
".wvx": "video/x-ms-wvx",
".fax": "image/fax",
".gif": "image/gif",
".jfif": "image/jpeg",
".jpeg": "image/jpeg",
".net": "image/pnetvue",
".rp": "image/vnd.rn-realpix",
".tiff": "image/tiff",
".wbmp": "image/vnd.wap.wbmp",
".eml": "message/rfc822",
".mht": "message/rfc822",
".mhtml": "message/rfc822",
".nws": "message/rfc822",
".907": "drawing/907",
".slk": "drawing/x-slk",
".top": "drawing/x-top",
".class": "java/*",
".java": "java/*"
}
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/middleware/router.js
================================================
const url = require('url');
module.exports = function(router) {
return async (ctx, next) => {
await next();
if(ctx.request.url.indexOf(router.prefixPath)!=0) {return;}
let urlList = url.parse(ctx.request.url).pathname.split('/');
if(!router.routerMap.has(urlList[2])) {return;}
let controller = router.routerMap.get(urlList[2]);
if(!(controller[urlList[3]] instanceof Function)) {return;}
try{
ctx.body = {
errCode:0,
errKey:"",
data:await controller[urlList[3]](ctx)
}
}catch(err) {
ctx.body = {
errCode:1,
errKey:"sys.sys_error",
}
process.stdout.write(err.stack);
}
}
}
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/app/middleware/static.js
================================================
const path = require('path');
const applicationType = require('./application-type.js');
const fs = require('fs');
function readFile(filePath) {
return new Promise((resolve,reject)=>{
fs.readFile(filePath, (err, data) => {
if (err) reject(err);
resolve(data);
});
})
}
function switchTypeByExt(extname) {
if(applicationType[extname]) {
return applicationType[extname];
}
return false;
}
module.exports = function(static) {
return async (ctx,next) => {
for(let staticItem of static) {
if(ctx.request.url.indexOf(staticItem.urlPrefix)!=0) {return;}
for(let defaultItem of ['',...staticItem.defaultList]) {
let filePath = path.join(staticItem.staticPath,ctx.request.url,defaultItem);
if(!fs.existsSync(filePath)) {continue;}
let fileStats = fs.statSync(filePath);
if(!fileStats.isFile()) {continue;}
let extname = path.extname(filePath);
let type = switchTypeByExt(extname);
if(type) {
ctx.type = type;
}
ctx.body = (await readFile(filePath)).toString();
// ctx.body = fs.createReadStream(filePath);// 这里仅仅适用于大文件下载
}
}
await next();
}
}
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/main.js
================================================
const Koa = require('koa');
const path = require('path');
const config = require('./app/config');
/* 为全干定制的中间件 */
const router = require('./app/middleware/router');
const static = require('./app/middleware/static');
/* koa实例初始化 */
const app = new Koa();
app.use(static(require('./app/back/static')));
app.use(router(require('./app/back/router')));
app.listen(config.port, config.hostname);
/* 前端启动 */
require('child_process').fork('run.js',process.argv,{cwd:path.join(__dirname,'app','front')});
================================================
FILE: article/front/make-full-stack-framework/full-stack-demo/package.json
================================================
{
"name": "full-stack-demo",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"dev": "npx nodemon main.js",
"stable": "node main.js --stable",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"koa": "^2.7.0",
"my-i18n": "^1.0.4",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.6",
"nodemon": "^1.19.1",
"webpack": "^4.37.0"
},
"nodemonConfig": {
"ignore": [
"app/front/*"
]
},
"author": "zy445566",
"license": "MIT"
}
================================================
FILE: article/front/whyneedframework/README.md
================================================
# 为什么我们还要使用前端框架来构建页面?
今天一个同事说现在前端一定要使用前端框架,现在复杂应用不用就写不下去。我说我们很多场景下未必就一定需要前端框架,很多时候没有框架反而会更好。并且我相信随着Web规范的一步一步完善,去框架化的时代或许不久就会到来。
我列举了几个使用前端框架的坏处:
* 给每个页面无端带上了多余的上百K的请求负担
* 过于依赖框架,让我们成为框架工程师
* 对理解底层JS实现增加障碍
* 框架对代码进行编译和封装,让我们在棘手问题的调试更加困难
同时列举无框架化该如何实现组件的封装,路由跳转,双向数据绑定,列表渲染!
`注意:本文基于chrome66以上版本!!!`
本文例子代码地址:[点此打开本文example代码](https://github.com/zy445566/myBlog/tree/master/20191023front/20191023whyneedframework/example)
# 构造一个具有封装性的组件
首先我们需要增加一个index.html,里面添加一个自定义标签app-container,作为我们的app容器,同时引用一个模块化的js文件用type="module"来区分,如下:
```html
<!DOCTYPE HTML>
<html>
<head>
<title>show time</title>
</head>
<body>
<app-container />
<script type="module" src="/app.js"></script>
</body>
</html>
```
接下来我们来完成app.js,首先我们需要创建一个模版,其中包含了自定义路由标签,但不需要管,后面会说到路由的实现。
```js
const template = document.createElement('template');
template.innerHTML = `
<style>
.container > h1 {
width:120px;
border: solid;
cursor: pointer;
}
</style>
<div class="container">
<h1>Click Me!</h1>
<my-router>
<my-browse-route path="/data-bind" tag="data-bind"></my-browse-route>
<my-browse-route path="/add-list" tag="add-list"></my-browse-route>
</my-router>
</div>`;
```
然后把创建的模版来生成一个关闭的封装模式的web组件,同时使用customElements.define来注册自定义的web组件,到这里app-container标签就成功生成为一个web组件完成封装。 如下是app.js的代码:
```js
// 省略一些代码
class AppContainer extends HTMLElement {
constructor() {
super();
const template = document.createElement('template');
template.innerHTML = `...模版代码,这里省略`;//就是上面的代码,省略
const content = template.content.cloneNode(true);
// 这里是设定封装模式,可以设置是否能受到外界因素影响
const shadow = this.attachShadow( { mode: 'closed' } );
// 省略一些代码...
shadow.appendChild(content);
}
}
window.customElements.define('app-container', AppContainer);
```
# 一个路由跳转组件的实现
如app.js所示的路由标签如下所示,那么我们需要做两个组件分别是my-router和my-browse-route。
```html
<my-router>
<my-browse-route path="/data-bind" tag="data-bind"></my-browse-route>
<my-browse-route path="/add-list" tag="add-list"></my-browse-route>
</my-router>
```
那么我们第一个路由组件my-router只需要一个空壳来包装路由文件的内容,而my-browse-route则需要根据目前的路由来显示内容。
那么一个空壳组件my-router其实只需要,如下实现:
```js
export default class MyRouter extends HTMLElement {
constructor() {
super();
}
}
```
当然组件my-browse-route也只需要寥寥代码即可实现:
```js
export default class MyBrowseRoute extends HTMLElement {
constructor() {
super();
const template = document.createElement('template');
// 获取path属性来决定当前路由是否渲染内容
this.path = this.getAttribute('path');
// 获取tag属性来决定显示那个组件
this.tag = this.getAttribute('tag');
template.innerHTML = this.getHtml()
const content = template.content.cloneNode(true);
const shadow = this.attachShadow( { mode: 'closed' } );
shadow.appendChild(content);
}
getHtml() {
// 这里是如果当前路由不等于设置路由则直接返回空html来渲染
if(window.location.pathname!=this.path) {return ''};
return `<${this.tag}/>`
}
}
```
# 实现双向绑定的例子
其实双向绑定实现的方式有很多,我使用了一个代码量较少的方式来实现,即一开始就劫持数据,并在数据变化的时候,重新渲染模版如下:
```js
export default class DataBind extends HTMLElement {
constructor() {
this.data = this.dataBind({
inputVal:'hello'
});
// 省略若干代码...
}
dataBind(data) {
// 这里进行数据劫持
return new Proxy(data, {
set: (target, key, receiver) => {
Reflect.set(target, key, receiver)
// 这里进行重新渲染模版
this.shadow.innerHTML = this.getHtml();
return Reflect.set(target, key, receiver);
}})
}
getHtml() {
// 这里获取数据的最新模版
return `
<di>
<input type="text" value="${this.data.inputVal}"/>
<h4>${this.data.inputVal}</h4>
</div>`;
}
}
```
# 列表渲染的实现
列表渲染更是简单,只需要利用一下模版字符串的一些小技巧既可以实现列表渲染,如下。
```js
export default class AddList extends HTMLElement {
constructor() {
// 这里设置默认值
this.data = {
inputVal:'hello',
list:[]
}
// 省略一些代码...
const content = template.content.cloneNode(true);
const myUl = content.querySelector('ul');
// 这里根据按钮点击重新渲染列表
myBtn.addEventListener('click',()=>{
if(myInput.value) {
this.data.list.push(myInput.value);
myUl.innerHTML = this.getLi()
}
})
// 省略一些代码...
}
// 这里就是遍历list数据来重新渲染列表模版
getLi() {
return `${this.data.list.map((val)=>{
return `<li>${val}</li>`
}).join('')}`
}
}
```
如果想要集成数据绑定和模版渲染写个基类,然后上层都继承基类即可。
# 总结
不使用前端框架而使用web组件来替代的好处就是简单的页面基本都是几十B(是B不是KB)就搞定,在渲染的时候自己可控性也强导致性能可以优化的更猛,更原生的体验,不需要热加载,刷新即可见。debugger都是原生代码而不是框架处理后的代码,摆脱框架束缚。自己搭框架也不过是百行,同时随着方法即服务流行扩大,这种无框架架构将会受到更大的支持。
`注意:本文基于chrome66以上版本!!!`
本文例子代码地址:[点此打开本文example代码](https://github.com/zy445566/myBlog/tree/master/20191023front/20191023whyneedframework/example)
================================================
FILE: article/front/whyneedframework/example/README.md
================================================
# example
```sh
cd 20191023front/20191023whyneedframework/example/
npm install
npm run start
```
================================================
FILE: article/front/whyneedframework/example/app.js
================================================
import './element-registry.js';
class AppContainer extends HTMLElement {
constructor() {
super();
const template = document.createElement('template');
template.innerHTML = `
<style>
.container > h1 {
width:120px;
border: solid;
cursor: pointer;
}
</style>
<div class="container">
<h1>Click Me!</h1>
<my-router>
<my-browse-route path="/data-bind" tag="data-bind"></my-browse-route>
<my-browse-route path="/add-list" tag="add-list"></my-browse-route>
</my-router>
</div>`;
const content = template.content.cloneNode(true);
const shadow = this.attachShadow( { mode: 'closed' } );
const paths = ['/', '/data-bind', '/add-list'];
content.querySelector('h1').addEventListener('click',()=>{
let pathIndex = paths.indexOf(window.location.pathname);
pathIndex=(pathIndex==paths.length-1?0:pathIndex+1);
window.location.pathname = paths[pathIndex]
})
shadow.appendChild(content);
}
}
window.customElements.define('app-container', AppContainer);
================================================
FILE: article/front/whyneedframework/example/components/AddList.js
================================================
export default class AddList extends HTMLElement {
constructor() {
super();
const template = document.createElement('template');
this.data = {
inputVal:'hello',
list:[]
}
template.innerHTML = this.getHtml()
const content = template.content.cloneNode(true);
const shadow = this.attachShadow( { mode: 'closed' } );
const myInput = content.querySelector('input');
const myBtn = content.querySelector('button');
const myUl = content.querySelector('ul');
myBtn.addEventListener('click',()=>{
if(myInput.value) {
this.data.list.push(myInput.value);
myUl.innerHTML = this.getLi()
}
})
shadow.appendChild(content);
}
getHtml() {
return `
<di>
<input type="text" value="${this.data.inputVal}"/><button type="button">add</button>
<ul>${this.getLi()}</ul>
</div>`;
}
getLi() {
return `${this.data.list.map((val)=>{
return `<li>${val}</li>`
}).join('')}`
}
}
================================================
FILE: article/front/whyneedframework/example/components/DataBind.js
================================================
export default class DataBind extends HTMLElement {
constructor() {
super();
const template = document.createElement('template');
this.data = this.dataBind({
inputVal:'hello'
});
template.innerHTML = this.getHtml();
const content = template.content.cloneNode(true);
this.shadow = this.attachShadow( { mode: 'closed' } );
this.shadow.appendChild(content);
this.addEvent()
}
dataBind(data) {
return new Proxy(data, {
set: (target, key, receiver) => {
Reflect.set(target, key, receiver)
this.shadow.innerHTML = this.getHtml();
this.addEvent();
return Reflect.set(target, key, receiver);
}})
}
addEvent() {
this.myInput = this.shadow.querySelector('input');
this.myInput.addEventListener('keyup',(e)=>{
this.changeVal(e)
})
}
changeVal(e) {
this.data.inputVal = e.target.value;
this.myInput.selectionStart = e.target.selectionStart;
this.myInput.selectionEnd = e.target.selectionEnd;
this.myInput.focus();
}
getHtml() {
return `
<di>
<input type="text" value="${this.data.inputVal}"/>
<h4>${this.data.inputVal}</h4>
</div>`;
}
}
================================================
FILE: article/front/whyneedframework/example/components/MyBrowseRoute.js
================================================
export default class MyBrowseRoute extends HTMLElement {
constructor() {
super();
const template = document.createElement('template');
this.path = this.getAttribute('path');
this.tag = this.getAttribute('tag');
template.innerHTML = this.getHtml()
const content = template.content.cloneNode(true);
const shadow = this.attachShadow( { mode: 'closed' } );
shadow.appendChild(content);
}
getHtml() {
if(window.location.pathname!=this.path) {return ''};
return `<${this.tag}/>`
}
}
================================================
FILE: article/front/whyneedframework/example/components/MyRouter.js
================================================
export default class MyRouter extends HTMLElement {
constructor() {
super();
}
}
================================================
FILE: article/front/whyneedframework/example/element-registry.js
================================================
import MyRouter from './components/MyRouter.js';
import MyBrowseRoute from './components/MyBrowseRoute.js';
import AddList from './components/AddList.js';
import DataBind from './components/DataBind.js';
window.customElements.define('my-router', MyRouter);
window.customElements.define('my-browse-route', MyBrowseRoute);
window.customElements.define('data-bind', DataBind);
window.customElements.define('add-list', AddList);
================================================
FILE: article/front/whyneedframework/example/index.html
================================================
<!DOCTYPE HTML>
<html>
<head>
<title>show time</title>
</head>
<body>
<app-container />
<script type="module" src="/app.js"></script>
</body>
</html>
================================================
FILE: article/front/whyneedframework/example/package.json
================================================
{
"name": "example",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "npx serve -s ."
},
"author": "zy445566",
"license": "MIT",
"dependencies": {
"serve": "^11.2.0"
}
}
================================================
FILE: article/git/README.md
================================================
# git
* [github假面技术(20180929)](../git/git-fake/README.md)
* [如何解决多仓库项目的使用问题(20210525)](../git/gitmodules/README.md)
================================================
FILE: article/git/git-fake/README.md
================================================
# 让大佬向你仓库PR关键技术,拿去装逼,不要谢我
前段时间看到可以通过修改git的邮箱实现,实现伪造记录,但是修改起来还挺麻烦的,大佬的账号不能在命令行中保存,遂做了一个命令行工具,专门用来伪造github的提交请求。😎
# 先看效果
> 伪造了当年TJ大佬的提交记录

# 是不是很想要TJ大神的力量
来吧,全局安装完,直接敲命令就能实现
```sh
# 全局安装 git-fake
npm install git-fake -g
# 看看是否安装成功
gitfake -V
# 在项目根目录使用TJ的邮箱(只对该项目生效)
# 如果要伪造别人的账号,直接在github看邮箱即可
gitfake -u tj@apex.sh
# 对于使用过的账号,直接就能选择
gitfake -c
```
剩下的工作就是在这个项目提交代码了,提交完成后可以使用"git log"命令查看提交记录的邮箱是否被修改,如下 <br />

# 缺点
不过假的终究是假的,努力提升自己水平才是王道啊
================================================
FILE: article/git/gitmodules/README.md
================================================
# 如何解决多仓库项目的使用问题
最近接手一个老项目,刚刚准备git clone的时候,我人傻了,这个项目大概有十几个子仓库,之前维护的同学告诉我让我一个仓库一个仓库自行git clone下来,然后在依次对每个项目安装依赖,最后放到指定的目录结构再运行起来。
git clone了两个仓库后,我停了下来想到如果每个人都跟着这个方案走,下一个接手的人还要接受这样的痛苦,我认为这是不负责的,所以我决定做一下仓库整合。
## 那如何解决呢?
我的思路是这样的,先使用创建一个主仓库来承载全部的子仓库,然后针对重复依赖安装则使用软链的方式来完成。可能你会觉得这很复杂,但实际上现在已经有很成熟的方法和工具可以来做这些事情了。
## 祭出神器gitmodules
首先创建了一个新的仓库作为主仓库,然后再使用git submodule add 命令添加子仓库,如:
```sh
git submodule add git@git.xxxx.com:xxxx/xxxx.git
```
然后将全部子仓库,都设置完成后,测试一下是否能拉取成功
```sh
git clone --recursive git@git.xxxx.com:xxxx/main.git
```
这里解释一下recursive参数的用途,在我们拉取主仓库的时候,默认是不会拉取子仓库数据的,所以使用recursive来递归拉取子仓库。
这时你可能会发现仓库拉取下来都不是具体的分支名字而不是分支ID,这样的话子模块是无法被提交的,所以你可以使用git submodule foreach来做这件事情。
```sh
git submodule foreach git checkout master
```
git submodule foreach是可以在仓库中运行任意命令,上面的意思是让全部子仓库切换到master分支,你还可以使用其他命令如:
```sh
git submodule foreach ls
```
这里则是让每个子仓库进行ls操作,列出每个子仓库下面的文件名字列表。
OK,走到这里好像所有事情都结束了?
然而并不是,因为我们需要对每个子仓库都进行依赖安装,可能有同学说直接使用git submodule foreach npm install不就好了。当然不行,这些仓库依赖都很重复,如果对每个仓库进行install会极大增加安装依赖的时间。所以我们还需要一样东西。
## 再祭出神器lerna
lerna是什么呢?这个当时是babel团队为了解决babel中多模块发布而推出的一个神器来更好管理monorepo。
那么我们针对多仓库依赖重复安装问题,当然也可以用这把刀来做。
首先我们创建lerna.json,用来标识仓库和子模块,我这边是这样做的,仓库有省略。
```json
{
"packages": [
"accidents",
"crm",
"dailyReport",
"mammut",
"migration",
"obd",
"rules",
"sdkconfig"
],
"version": "1.0.0"
}
```
然后使用使用lerna的依赖安装功能对多个仓库进行依赖安装
```sh
npx lerna bootstrap
```
使用这种方式,就可以当有重复依赖的时候只使用软链的方式来安装依赖,不仅省空间也省时间了,同时lerna有更强大的monorepo管理功能,这里不多说,就请你自己探索了。
## 总结
最后强烈希望大家,在对多仓库使用的时候能尽可能使用submodule来管理再根据实际情况使用lerna做优化,也不是很麻烦,但是对后人维护来说却能够节约很多沟通成本和搭建时间。希望本篇文章能够帮助大家更好地管理多仓库,感谢大家阅读。
================================================
FILE: article/http/README.md
================================================
# web
* [为什么在HTTP的chunked模式下不需要设置长度(20190515)](../http/http-chunked/README.md)
* [使用nodejs实现服务端websocket通讯(20200409)](../http/node-web-socket/README.md)
================================================
FILE: article/http/http-chunked/README.md
================================================
# 为什么在HTTP的chunked模式下不需要设置长度
昨天看到论坛上有位同学提问,为什么Transfer-Encoding为chunked的时候不需要设置content-length?那为什么非要设置content-length呢?这位同学认为HTTP中内容长度是可有可无没必要设置的。真的是这样吗?chunked模式下真的就没有设置长度吗?
# 起因
一位同学看到《Http权威指南》p131下面写到,响应实体应该有content-type,content-length。但是这位同学在学习node时,发现他写的服务器并没有满足这两个条件。
他通过菜鸟教程的例子:
```js
var http = require('http');
http.createServer(function (request, response) {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// 发送响应数据 "Hello World"
response.end('Hello World\n');
}).listen(8888);
// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');
```
发现PostMan中并没有发现content-type和content-length,即认为服务器并没有给出内容长度,如下:

`画外音`:这个问题是思考过后的结果,我认为这个问题提非常棒,我非常期待更多像这样富有思考的问题。同时感谢这位同学的提问和素材。
# 但真的chunked没有给出长度吗?
当然不是,chunked给出的长度是在内容的报文当中的,我们可以用socket简单实现一下http1.1的chunked。
实现如下:
```js
// 直接使用socket简单实现http1.1协议chunked部分
const net = require('net');
const port = 3000;
net.createServer((socket) => {
socket.on('data',(data)=>{
let msg = data.toString();
console.log(msg) //读取的客户端请求数据
socket.write('HTTP/1.1 200 OK\r\n');
socket.write(`Date: ${new Date().toGMTString()}T\r\n`);
socket.write('Content-Type: text/html\r\n');
socket.write(`Transfer-Encoding: chunked\r\n`);
// 这里是16进制数字,表示后面有12字节数据
socket.write(`\r\n${(12).toString(16)}\r\n`);
socket.write('Hello World!');
socket.write(`\r\n0\r\n\r\n`); // say Bye-Bye
});
}).listen(port);
console.log(`use: curl -v http://127.0.0.1:${port}`)
```
在这里面你可以看到,这里就是声明了chunk的字节长度
```js
socket.write(`\r\n${(12).toString(16)}\r\n`);
```
同时最后say Bye-Bye的时候,声明了后续的长度为0,最后已回车确认结束
那么content-length和chunked有什么区别呢?
即是声明了content-length需要将内容报文一次给出,而chunked不用。
比如我修改一下代码:
```js
// 直接使用socket简单实现http1.1协议chunked部分
const net = require('net');
const port = 3000;
net.createServer((socket) => {
socket.on('data',(data)=>{
let msg = data.toString();
console.log(msg) //读取的客户端请求数据
socket.write('HTTP/1.1 200 OK\r\n');
socket.write(`Date: ${new Date().toGMTString()}T\r\n`);
socket.write('Content-Type: text/html\r\n');
socket.write(`Transfer-Encoding: chunked\r\n`);
// 这里是16进制数字,表示后面有12字节数据
socket.write(`\r\n${(12).toString(16)}\r\n`);
socket.write('Hello World!');
// ### 注意这是新增的部分 ###
socket.write(`\r\n${(4).toString(16)}\r\n`);
// ### 注意这是新增的部分 ###
socket.write('Boy!');
socket.write(`\r\n0\r\n\r\n`); // say Bye-Bye
});
}).listen(port);
console.log(`use: curl -v http://127.0.0.1:${port}`)
```
注意我新增的部分。chunked可以利用不断的追加内容在尾部增加报文的长度,实现段传输,最后完成后合成一个完整的http。但content-length在开始就定义好了长度,所以content-length不能像chunked一样,一直在尾部增加长度和内容。
# 结论
chunked还是给浏览器传输了长度,但是偷偷藏在了报文当中,所以并没有显式地像content-length在头部声明。当然也不能说《Http权威指南》是错误的,也许这本书有一定的年纪了呢?毕竟chunked是http1.1才有的,http1.0还不存在。
================================================
FILE: article/http/node-web-socket/README.md
================================================
# 使用nodejs实现服务端websocket通讯
起因是在写一个前置监控服务项目,需要数据相对实时的传输,然后正好看到nodejs文档中,实现websocket看起来挺简单的(其实只是冰山一角还有坑),所以就打算自己实现一遍websocket通讯服务。先看看nodejs官方文档怎么实现的:
```js
// nodejs在http模块实现websocket的例子
const http = require('http');
// Create an HTTP server
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('okay');
});
server.on('upgrade', (req, socket, head) => {
socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
'Upgrade: WebSocket\r\n' +
'Connection: Upgrade\r\n' +
'\r\n');
socket.pipe(socket); // echo back
});
```
第一眼以为通过upgrade拿到socket套接字,然后就可以直接用socket.write和socket.on('data')的方法来发送和获取数据。但事实并不是这样。
# 第一坑:Sec-WebSocket-Accept
我在浏览器中写好websocket的例子:
```js
var ws = new WebSocket(`ws://${window.location.host}/`);
ws.onopen = function()
{
console.log("握手成功");
ws.send("发送数据测试");
};
ws.onmessage = function (e)
{
console.log(e.data);
};
```
结果一连接就断开,说我没有Sec-WebSocket-Accept这个http头,网上一查一点结果都没有,看来实现这个的确实不多。
找来找去终于找到了websocket的协议文档(https://tools.ietf.org/html/rfc6455)。
发现Sec-WebSocket-Accept这个返回头是根据客户端的请求头sec-websocket-key,加上全局唯一ID(258EAFA5-E914-47DA-95CA-C5AB0DC85B11)后使用sha1摘要后,再以base64格式输出。
```js
const crypto=require('crypto')
function getSecWebSocketAccept (secWebsocketKey){
return crypto.createHash('sha1')
.update(`${secWebsocketKey}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`)
.digest('base64');
}
const secWebSocketAccept = getSecWebSocketAccept(req.headers['sec-websocket-key'])
socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
'Upgrade: WebSocket\r\n' +
'Connection: Upgrade\r\n' +
'Sec-WebSocket-Accept: '+ secWebSocketAccept +'\r\n' +
'\r\n');
```
再刷新下浏览器,发现握手成功了。
# 第二坑:接收到的客户端数据是乱码
握手成功后,肯定是要看客户端给我发了什么数据,原来是个buffer,但toString后居然是乱码。
```js
socket.on('data', (data) => {
console.log(data.toString())
});
```
当时就在想里面是不是有猫腻,一看果然websocket还有frame的概念,接收到data就是一个frame,在这个框架里面有一定的结构。
在文档中叫Base Framing Protocol(https://tools.ietf.org/html/rfc6455#section-5.2),大概的结构如下:
```js
/**
我在第二三行重新加了个按字节和比特来计算的比例尺
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1 2 3 4
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
*/
```
什么意思呢?那么按照我标记的字节来算吧
```
---
第1个字节的第1个比特是FIN的值,用来标识这个frame信息传递是否结束,1是结束
第1个字节的第2-3个比特是RSV的值,用来标识这个frame信息传递是否结束
第1个字节的第4-8个比特是opcode的值,来标记状态1是文本数据2是二进制数据8是请求关闭链接
---
第2个字节的第1个比特是Mask的值,用来标识数据是否使用Masking-key来做异或解码
第2个字节的第2-8个比特PayloadLen,
代表数据长度,如果为126,则使用16位的扩展数据长
代表数据长度,如果为127,则使用8位的扩展数据长度
扩展长度使用大字端读取就好
```
那知道这些就可以通过代码来实现解码,代码实现如下
```js
function decodeSocketFrame (bufData){
let bufIndex = 0
const byte1 = bufData.readUInt8(bufIndex++).toString(2)
const byte2 = bufData.readUInt8(bufIndex++).toString(2)
const frame = {
fin:parseInt(byte1.substring(0,1),2),
// RSV是保留字段,暂时不计算
opcode:parseInt(byte1.substring(4,8),2),
mask:parseInt(byte2.substring(0,1),2),
payloadLen:parseInt(byte2.substring(1,8),2),
}
// 如果frame.payloadLen为126或127说明这个长度不够了,要使用扩展长度了
// 如果frame.payloadLen为126,则使用Extended payload length同时为16/8字节数
// 如果frame.payloadLen为127,则使用Extended payload length同时为64/8字节数
// 注意payloadLen得长度单位是字节(bytes)而不是比特(bit)
if(frame.payloadLen==126) {
frame.payloadLen = bufData.readUIntBE(bufIndex,2);
bufIndex+=2;
} else if(frame.payloadLen==127) {
// 虽然是8字节,但是前四字节目前留空,因为int型是4字节不留空int会溢出
bufIndex+=4;
frame.payloadLen = bufData.readUIntBE(bufIndex,4);
bufIndex+=4;
}
if(frame.mask){
const payloadBufList = []
// maskingKey为4字节数据
frame.maskingKey=[bufData[bufIndex++],bufData[bufIndex++],bufData[bufIndex++],bufData[bufIndex++]];
for(let i=0;i<frame.payloadLen;i++) {
payloadBufList.push(bufData[bufIndex+i]^frame.maskingKey[i%4]);
}
frame.payloadBuf = Buffer.from(payloadBufList)
} else {
frame.payloadBuf = bufData.slice(bufIndex,bufIndex+frame.payloadLen)
}
return frame
}
```
那如果你解码数据,那么发送的时候其实也是要遵循这种基本框架的,所以还要进行加码成frame框架后再发送,同理根据协议,可以实现如下代码:
```js
function encodeSocketFrame (frame){
const frameBufList = [];
// 对fin位移七位则为10000000加opcode为10000001
const header = (frame.fin<<7)+frame.opcode;
console.log(header)
frameBufList.push(header)
const bufBits = Buffer.byteLength(frame.payloadBuf);
let payloadLen = bufBits;
let extBuf;
if(bufBits>=126) {
//65536是2**16即两字节数字极限
if(bufBits>=65536) {
extBuf = Buffer.allocUnsafe(8);
buf.writeUInt32BE(bufBits, 4);
payloadLen = 127;
} else {
extBuf = Buffer.allocUnsafe(2);
buf.writeUInt16BE(bufBits, 0);
payloadLen = 126;
}
}
let payloadLenBinStr = payloadLen.toString(2);
while(payloadLenBinStr.length<8){payloadLenBinStr='0'+payloadLenBinStr;}
frameBufList.push(parseInt(payloadLenBinStr,2));
if(bufBits>=126) {
frameBufList.push(extBuf);
}
frameBufList.push(...frame.payloadBuf)
return Buffer.from(frameBufList)
}
```
那么我们发送和接受就简单了,直接通过socket再发送就好了,如下
```js
socket.on('data', (data) => {
console.log(decodeSocketFrame(data).payloadBuf.toString())
socket.write(encodeSocketFrame({
fin:1,
opcode:1,
payloadBuf:Buffer.from('你好')
}))
});
```
# 总结
其实websocket和http对于socket来说都是在上面加了一层协议,通过不同方法来实现其功能,随着技术的发展,协议也确实往复杂的方向发展。
在工作种如果自己实现协议可能就太费时间了,但是如果是非工作,实现一遍也还是能收获良多的。
================================================
FILE: article/leetcode/README.md
================================================
# 算法
* [动态规划包quick-dp的拆解和使用(20180531)](../leetcode/dynamic-programming/README.md)
* [三数之和的优化之旅(20180926)](../leetcode/three-sum/README.md)
* [解数独(20181023)](../leetcode/sudoku-solver/README.md)
* [用买卖股票的最佳时机理解动态规划(20181220)](../leetcode/best-time-to-buy-and-sell-stock/README.md)
* [一道算法题引发布隆过滤器原理的思考(20210326)](../leetcode/bloom-filter/README.md)
* [分享一个构建树的方法(20240322)](../leetcode/build-tree/README.md)
================================================
FILE: article/leetcode/best-time-to-buy-and-sell-stock/README.md
================================================
# 动态规划难?读完这篇还不理解那就不要请我吃鸡了
昨天同事说动态规划很难,我说不会啊,理解了就很简单,我同事表示不屑,以为我在炫技。于是乎我问了一个工作六年的前同事,他居然也觉得高大上,并且表示接触过会动态规划的朋友,觉得很牛逼。
我了个天,表示震惊了,简直吓的我瑟瑟发抖发抖好么。既然如此,那我一定要让大家理解,让大家也牛逼牛逼。
# 以题举例
以中国leetcode(力扣)的121道题《买卖股票的最佳时机》举例,题目如下:
```
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
```
首先说明一点,在这道题动态规划并不算是高效的算法,这道题即使是暴力法也能比动态规划法要快一些,但本文是为了讲动态规划才讲这道题的,而非为了这道题讲动态规划的。纯粹是这道题讲动态规划更简单。
不了解动态规划的朋友可能不知道,动态规划几乎都是存在套路的,步骤如下:
1. 将问题拆解成单一问题制表
2. 根据表中结果进行求解
# 第一步:将问题拆解成单一问题制表
这是动态规划的重中之重,拆的好坏基本决定你动态规划写的好坏,动态规划更是一种思想。
这道题就很简单了,我们可以把这个问题,先拆成单一问题,即`如果我在这天买入,在哪天卖出最高?`
我们按题目中的示例 1举例,价格数组为[7,1,5,3,6,4]。
比如我在第一天买入,在哪天卖出最高呢?我们可以得到这样一张表(不能交易标记X):
| 买入时间 | 买入价格 | 单天价格 | 7 | 1 | 5 | 3 | 6 | 4 |
| ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| -- | -- | 卖出时间 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
| 第一天 | 7 | 获利程度 | X | -6 | -2 | -4 | -1 | -3 |
填表原因如下:
* 即因为你是第一天买入的,所以第一天不能交易,所以填X
* 我们第一天买入的,买入价格为7,第二天值1,所以我们卖出则赚了-6.
* 我们第一天买入的,买入价格为7,第三天值5,所以我们卖出则赚了-2.
* 我们第一天买入的,买入价格为7,第四天值3,所以我们卖出则赚了-4.
* 我们第一天买入的,买入价格为7,第五天值6,所以我们卖出则赚了-1.
* 我们第一天买入的,买入价格为7,第六天值4,所以我们卖出则赚了-3.
`有人说这个不是暴力解法么?额,这道题的动态规划的制表过程确实有点像。`
那么根据以上规则,如果我们是第二天买入的话,这个表格是不是就是这样的。
| 买入时间 | 买入价格 | 单天价格 | 7 | 1 | 5 | 3 | 6 | 4 |
| ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| -- | -- | 卖出时间 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
| 第二天 | 1 | 获利程度 | X | X | 4 | 2 | 5 | 3 |
那么我合并这两张表是不是就是这样的:
| 买入时间 | 买入价格 | 单天价格 | 7 | 1 | 5 | 3 | 6 | 4 |
| ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| -- | -- | 卖出时间 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
| 第一天 | 7 | 获利程度 | X | -6 | -2 | -4 | -1 | -3 |
| 第二天 | 1 | 获利程度 | X | X | 4 | 2 | 5 | 3 |
那么我们把这张表弄完整,即把第三天到第五天的买入,那是不是就是这样的。
| 买入时间 | 买入价格 | 单天价格 | 7 | 1 | 5 | 3 | 6 | 4 |
| ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| -- | -- | 卖出时间 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
| 第一天 | 7 | 获利程度 | X | -6 | -2 | -4 | -1 | -3 |
| 第二天 | 1 | 获利程度 | X | X | 4 | 2 | 5 | 3 |
| 第三天 | 5 | 获利程度 | X | X | X | -2 | 1 | -1 |
| 第四天 | 3 | 获利程度 | X | X | X | X | 3 | 1 |
| 第五天 | 6 | 获利程度 | X | X | X | X | X | -2 |
| 第六天 | 4 | 获利程度 | X | X | X | X | X | X |
因为第六天买了的话,已经是最后一天了,所以没办法卖了,所以全是X。
# 第二步:根据表中结果进行求解
上一步,已经把如果是某天买的,每天的获利情况列了一遍,就是拆解成了单一问题。
那么看最大利润,很容易就能看出来了,就看那个数字最大就好了。
| 买入时间 | 买入价格 | 单天价格 | 7 | 1 | 5 | 3 | 6 | 4 |
| ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| -- | -- | 卖出时间 | 第一天 | 第二天 | 第三天 | 第四天 | 第五天 | 第六天 |
| 第一天 | 7 | 获利程度 | X | -6 | -2 | -4 | -1 | -3 |
| 第二天 | 1 | 获利程度 | X | X | 4 | 2 | 5⃣️ | 3 |
| 第三天 | 5 | 获利程度 | X | X | X | -2 | 1 | -1 |
| 第四天 | 3 | 获利程度 | X | X | X | X | 3 | 1 |
| 第五天 | 6 | 获利程度 | X | X | X | X | X | -2 |
| 第六天 | 4 | 获利程度 | X | X | X | X | X | X |
没错emoji的5的就是,那么我们只要简单的遍历一下这个表是不是就能把结果取出来了?`嗯,对的`
# 最后
但一般来说动态规划的题目不是这么简单的,通常在制表的时候会涉及一个叠加问题,在根据表计算结果一般也不是简单的遍历就能完成的,但理解动态规划思想应该是没什么问题了。
个人认为动态规划虽然性能不强,但是能把问题变得很直观,让人更简单的解决问题。同时算法的杂合度不高,很方便使用分布式为问题的每个单一问题进行求解。
本题代码:[best-time-to-buy-and-sell-stock.js](https://github.com/zy445566/myBlog/blob/master/20180926leetcode/20181220best-time-to-buy-and-sell-stock/best-time-to-buy-and-sell-stock.js)
================================================
FILE: article/leetcode/best-time-to-buy-and-sell-stock/best-time-to-buy-and-sell-stock.js
================================================
/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
// 优化头
while (prices.length>0) {
let maxPrice = Math.max.apply(this,prices);
if (maxPrice<=prices[0]) {
prices.shift();
} else {
break;
}
}
// 优化尾
while (prices.length>0) {
if (prices[prices.length-1]==0) {
prices.pop();
} else {
break;
}
}
// 开始动态规划填表
let dpTable = [];
for (let i = 0;i<prices.length;i++) {
dpTable[i] = [];
for (let j = 0;j<prices.length;j++) {
if (j<=i) {
dpTable[i].push('x')
} else {
dpTable[i].push(prices[j]-prices[i]);
}
}
}
// 开始动态规划求值
let maxValue = 0;
for (let i = 0;i<dpTable.length;i++) {
for (let j = i+1;j<dpTable[i].length;j++) {
if (dpTable[i][j]=='x') {continue;}
if (dpTable[i][j]>maxValue) {
maxValue = dpTable[i][j];
}
}
}
return maxValue;
};
================================================
FILE: article/leetcode/bloom-filter/README.md
================================================
# 一道算法题引发布隆过滤器原理的思考
## 起因
周会组内有一位同事L分享了一道算法题,题目是这样的:
```txt
A 文件有 50 亿条 URL,B 文件也有 50 亿条 URL,每条 URL 大小为 64B,在一台只有 4G 内存的机器上,怎么找出 A、B 中相同的 URL?
```
我当时脱口而出,这用布隆过滤器不就好了。
同事L看起来并没有太理会我的答案转而听取分治类的答案。我心想看看你有啥比我更好的答案,嘿嘿嘿!(安静做只腹黑猫🐱)
后来同事L,公布答案是这样的:
```txt
50 亿条 URL 文件的大小:50 * 10^8 * 64 Byte ≈ 32 * 10^10 Byte ≈ 320G
首先遍历 A 文件(注意逐行读取)
对每个 URL 取 hash(url)%1000 的值,该值就是 URL 要存储到小文件的编号,最终所有 URL 分别存储在 1000 个小文件中,文件名记成以下形式: a0、a1、…、a999
再遍历 B 文件,用同样的方法将 B 文件的 URL 分别存储在 b0、b1、…、b999 这 1000 个小文件中。
我们知道 Hash 的一个特点:相同的 key,hash 之后的 value 一定是相同的。所以,对于 A、B 文件中相同的 URL,Hash 之后,一定会存储到相同下标的文件中。
如果 Hash 函数设计合理,将数据均匀分散,每个文件大致 300M。
```
这时我坐不住了,我说这个方案那还不如使用布隆过滤器啊!
这时同事L发问,那啥是布隆过滤器?给我解释解释呗?
其他同事眼睛瞪的像铜铃(没错就是黑猫警长),似乎很想知道啥是布隆过滤器。。。
## 啥是布隆过滤器?
### 关于布隆过滤器的最小例子
大家知道计算机世界是由0和1组成的,那么储存0或1的数据的最小单元是位(bit)。那么布隆过滤器要初始化其实就是初始化N个bit的内存块。那么假设我们要初始化一个长度为11的布隆过滤器,那么它在内存中的表示就如下所示:
| 地址0 | 地址1 | 地址2 | 地址3 | 地址4 | 地址5 | 地址6 | 地址7 | 地址8 | 地址9 | 地址10 |
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
| 0 | 0 |0 | 0 |0 | 0 | 0 |0 | 0 |0 |0 |
那么这里就是一个11bit的布隆过滤器了,当我们要找出10以下数组中的相同的值,那么是不是可以直接对11取模。比如我们要找出下列数组中相同的值。
```js
[0, 1, 2, 2, 9]
```
那么首先是0%11=0,那么我们直接把`地址0`的0标记成1.
接下来是1%11=1,那么我们直接把`地址1`的0标记成1.
接下来是2%11=2,那么我们直接把`地址2`的0标记成1.
接下来是2%11=2,那么我们这时候发现`地址2`已经是1了,我们就直接把2存储起来,因为找到相同的数据了.
接下来是9%11=9,那么我们直接把`地址9`的0标记成1.
根据上面的逻辑,我们很轻松就找到相同的那个数2.
### 那么万一数据大怎么办?
布隆过滤器就是为大数据而生的,我给你算笔账:
```
1 byte = 8 bit
1 MB = 1024*1024*8 bit
1GB = 1024*1024*1024*8 bit
```
也就是说1MB的内存就能有8百万的bit,1GB就有80亿的bit。
那么回到我们的问题,我们只需要2GB内存就能创造160亿长度的布隆过滤器。
那么我们找2个50亿文件中相同的url就非常好解决了,找一个100亿左右的素数作为布隆过滤器的长度,计算每个url的hash值之后再对布隆过滤器的长度取模,如果发现布隆过滤器标记成1的数据,就可以几乎认定相同即可,这样只要遍历一遍且花费2GB不到的内存空间即可。当然碰撞概率是存在的,所以还需要考虑容忍度问题。
说罢,同事响起了雷鸣般的掌声(好吧,这个掌声是我脑补的,当时除了我一个人很激烈的说话,整体还是略显平淡,不过最终还是取得了同事L的认同)。
## 最后
由于同事看了一些网上关于布隆过滤器的文章感觉不是很好理解,所以正好遇到了这样有意思的分享后,所以本人决定重新理一理布隆过滤器的原理给大家,我们是前端公共体验产品技术部公共平台组的同事,希望这篇科普文能够更好的帮助理解布隆过滤器,谢谢大家。
================================================
FILE: article/leetcode/build-tree/README.md
================================================
# 分享一个构建树的方法
## 起因
想不到在2024年,我居然会重新开始刷题,但是作为JavaScript开发,在刷到树的时候,还是特别烦恼,倒也不是因为难而烦恼,而是因为刷题的时候要调试树的结构而烦恼,所以自己写了个方法来进行数的构建。但是想到链表有时也需要构建,想想干脆就一起写了吧
# 树的构建
树的构建,这里使用了BFS广度遍历来进行构建,主要原因是数组本身也是一个广度遍历型的数组结构
```js
function TreeNode(val, left, right) {
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
function buileTreeByArray(list) {
if(!list.length){return null}
let root = new TreeNode(list.shift())
const queue = [root]
while(list.length) {
const len = queue.length
for(let i=0;i<len;i++) {
const node = queue.shift()
if(node===null) {continue}
let leftVal = list.shift()
node.left = leftVal===null?null:new TreeNode(leftVal)
const rightVal = list.shift()
node.right = rightVal===null?null:new TreeNode(rightVal)
queue.push(node.left, node.right)
}
}
return root
}
```
# 链表的构建
链表的构建可以说丝毫没有难度,废话不多说,分享一下代码
```js
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
function buileListByArray(list) {
if(!list.length){return null}
let head = node = new ListNode(list.shift())
while(list.length) {
node.next = new ListNode(list.shift())
node = node.next
}
return head
}
```
# 结语
这几个方法本来是自用调试的,倒不是为了省钱不开通Plus会员,主要是身为技术还去开通leetcode的Plus会员就有点没技术范了。
================================================
FILE: article/leetcode/build-tree/code/list.js
================================================
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
function buileListByArray(list) {
if(!list.length){return null}
let head = node = new ListNode(list.shift())
while(list.length) {
node.next = new ListNode(list.shift())
node = node.next
}
return head
}
console.log(buileListByArray([1,2,3,4,5]))
================================================
FILE: article/leetcode/build-tree/code/tree.js
================================================
function TreeNode(val, left, right) {
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
function buileTreeByArray(list) {
if(!list.length){return null}
let root = new TreeNode(list.shift())
const queue = [root]
while(list.length) {
const len = queue.length
for(let i=0;i<len;i++) {
const node = queue.shift()
if(node===null) {continue}
let leftVal = list.shift()
node.left = leftVal===null?null:new TreeNode(leftVal)
const rightVal = list.shift()
node.right = rightVal===null?null:new TreeNode(rightVal)
queue.push(node.left, node.right)
}
}
return root
}
console.log(buileTreeByArray([1,2,3,null,4,5,6,null,null,7,null,null,8]))
================================================
FILE: article/leetcode/dynamic-programming/README.md
================================================
# 动态规划包quick-dp的拆解和使用
### 什么是动态规划
动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。<br />
可以说这不仅仅是一类算法,更是一种思想。
### 动态规划的意义
动态规划问世以来,在经济管理、生产调度、工程技术和最优控制等方面得到了广泛的应用。例如最短路线、库存管理、资源分配、设备更新、排序、装载等问题,用动态规划方法比用其它方法求解更为方便。<br />
虽然动态规划主要用于求解以时间划分阶段的动态过程的优化问题,但是一些与时间无关的静态规划(如线性规划、非线性规划),只要人为地引进时间因素,把它视为多阶段决策过程,也可以用动态规划方法方便地求解。<br />
也就是说在编程方面可以把一些复杂的问题简单化,使处理起来简单和清晰。<br />
### 快速使用动态规划来解决问题
我们可以使用 [https://www.npmjs.com/package/quick-dp](https://www.npmjs.com/package/quick-dp)这个库来简单的实现很多动态规划的问题。<br />
这次本人也根据quick-dp提供的例子之一《动态规划-找零钱》来讲解这个如何用这个库实现动态规划及这个库的实现,进而浅入动态规划的思想。
### 先看quick-dp的例子找零钱代码
#### 题目:
给予不同面值的硬币若干种种(每种硬币个数无限多),如何用若干种硬币组合为某种面额的钱,使硬币的的个数最少?
#### 代码:
```js
const DynamicProgramming = require('quick-dp');
// change coin question by dynamic programming
class Coin
{
constructor(much)
{
this.much = much;
}
}
let coinTypeList = [new Coin(1),new Coin(2),new Coin(5)];
let coinDP = new DynamicProgramming(coinTypeList,13);
let totalMoney = 0;
let coinResult = coinDP.run((item,itemKey,nowPurpose)=>{
return Math.ceil(nowPurpose/item.much);
},(item,itemResult,itemKey,nowPurpose,purpose,result)=>{
let money = item.much*itemResult;
if (money<=purpose-totalMoney)
{
result.push({coin:item,num:itemResult});
if (totalMoney+money==purpose){return DynamicProgramming.RETURN_FIND;}
totalMoney+=money;
return DynamicProgramming.BREAK_FIND;
}
});
console.log("change coin question:")
/**
* coinResult:
* [ { coin: Coin { much: 5 }, num: 2 },
* { coin: Coin { much: 2 }, num: 1 },
* { coin: Coin { much: 1 }, num: 1 } ]
*/
console.log(coinResult)
```
#### 代码解析
我们可以先看run是如何实现的
```js
run(getSingleResultFunc,findResultFunc)
{
let singleResultList = this.getSingleResultList(getSingleResultFunc);
return this.findResult(findResultFunc,singleResultList);
}
```
这里它调用了两个方法getSingleResultList和findResult。
也就是说我们把下面的方法传入了getSingleResultList来获取一个结果
```js
// getSingleResultFunc:
(item,itemKey,nowPurpose)=>{
return Math.ceil(nowPurpose/item.much);
}
```
那我们来看看getSingleResultList是如何实现的
```js
getSingleResultList(getSingleResultFunc)
{
let singleResultList = [];
for (let itemKey=0;itemKey<this.itemList.length;itemKey++)
{
singleResultList[itemKey] = [];
for(let nowPurpose=0;nowPurpose<=this.purpose;nowPurpose++)
{
singleResultList[itemKey][nowPurpose] = getSingleResultFunc(
this.itemList[itemKey],itemKey,nowPurpose,singleResultList
);
}
}
return singleResultList;
}
```
其实这里就是动态规划的核心思想“填表”。像上面的硬币就会生成如下表
```js
// singleResultList:
[ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ],
[ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 ],
[ 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3 ] ]
```
#### 那么生成这张表意义是什么呢?
仔细观察你会发现这张表就是单一结果的可能性。什么意思呢?<br />
这个例子是基于我们有1,2,5面值的硬币,要找13元的情况下的例子<br />
第一列就是只有面值为1时的找零的向上取整:<br />
```
只有面值为1在找零为0的时候要给出0个
只有面值为1在找零为1的时候要给出1个
只有面值为1在找零为2的时候要给出2个
只有面值为1在找零为3的时候要给出3个
...
只有面值为1在找零为11的时候要给出11个
只有面值为1在找零为12的时候要给出12个
只有面值为1在找零为13的时候要给出13个
```
第二列就是只有面值为2时的找零的向上取整:<br />
```
只有面值为2在找零为0的时候要给出0个
只有面值为2在找零为1的时候要给出1个(这里本来是要找0.5个,但向上取整了,后面以此类推)
只有面值为2在找零为2的时候要给出1个
...
只有面值为2在找零为11的时候要给出6个
只有面值为2在找零为12的时候要给出6个
只有面值为2在找零为13的时候要给出7个
```
第三列就和前两列雷同就不讲了,这里的目的就是对问题的拆分,将一些复杂的多情况问题先拆分成一个一个单一情况的问题
#### 那么拿到这张表之后呢?如何找结果呢?
拿到这张表之后其实就是可以开始组合这边的结果集了,看findResult是怎么实现的
```js
findResult(findResultFunc,singleResultList)
{
let result = [];
for (let itemKey=this.itemList.length-1;itemKey>=0;itemKey--)
{
for(let nowPurpose=this.purpose;nowPurpose>=0;nowPurpose--)
{
let isAgainRun = findResultFunc(
this.itemList[itemKey],
singleResultList[itemKey][nowPurpose],
itemKey,nowPurpose,
this.purpose,result,singleResultList
);
if (isAgainRun<0)
{
if (isAgainRun==DynamicProgramming.RETURN_FIND){return result;}
if (isAgainRun==DynamicProgramming.BREAK_FIND){break;}
if (isAgainRun==DynamicProgramming.CONTINUE_FIND){continue;}
}
}
}
}
```
我们这里看到,这就是一个倒叙遍历,也就是从最后一列和最后一行遍历到第一列第一行,同时根据findResultFunc返回的结果决定是否继续遍历还是中断操作等。。。那我们回头看我们传入的findResultFunc方法是怎么样的。
```js
(item,itemResult,itemKey,nowPurpose,purpose,result)=>{
let money = item.much*itemResult;
if (money<=purpose-totalMoney)
{
result.push({coin:item,num:itemResult});
if (totalMoney+money==purpose){return DynamicProgramming.RETURN_FIND;}
totalMoney+=money;
return DynamicProgramming.BREAK_FIND;
}
}
```
结合之前的倒序遍历,这里其实做的就是第一步最大硬币的面值乘以最大要找的数量,如果不是正好等于0,则向上让更小的硬币乘以更小的值,直到补足,使要找的钱正好到达我们的目标值,而已。<br />
使用动态规划,硬币找零只需要10行代码实现,quick-dp库的例子中还有01背包和动态规划排序的例子,有兴趣可以看一下。
### 使用quick-dp库实现动态规划的优缺点
#### 优点
* 能相对简单的实现动态规划
* 把单一循环工作都已经做了一遍
#### 缺点
* 动态规划的思想还是要自己给出
* 由于这个库为了兼容大多数动态规划,所以不会有太好的性能,针对特定的动态规划优化算法还是要自己写
================================================
FILE: article/leetcode/sudoku-solver/README.md
================================================
# 解数独
刚刷完《有效的数独》后就刷了《解数独》这道题。刷完后看到这道题被标记成`困难`,并且感觉还挺有意思的,所以正好拿出来分享一下。
# 先看看数独游戏的规则吧(玩过的可以忽略)
1. 数字 1-9 在每一行只能出现一次。
2. 数字 1-9 在每一列只能出现一次。
3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
待填入的数独:

完成的数独:

# 先看《有效的数独》解法
思路比较简单,就是遍历每一个值,并且分别判断横竖和对应该数的九宫格是否重复。这里不多说。
```js
/**
* @param {character[][]} board
* @return {boolean}
*/
var isValidSudoku = function(board) {
for(let i=0;i<board.length;i++) {
let map = {};
for (let j=0;j<board[i].length;j++) {
if (board[i][j]!='.'){
if (!map[board[i][j]]) {
map[board[i][j]] = true;
} else {
return false;
}
}
}
}
for(let i=0;i<board.length;i++) {
let map = {};
for (let j=0;j<board[i].length;j++) {
if (board[j][i]!='.'){
if (!map[board[j][i]]) {
map[board[j][i]] = true;
} else {
return false;
}
}
}
}
function littleReapeat(lStart,vStart,long = 3) {
let map = {};
for(let i=lStart;i<lStart+long;i++) {
for (let j=vStart;j<vStart+long;j++) {
if (board[i][j]!='.'){
if (!map[board[i][j]]) {
map[board[i][j]] = true;
} else {
return false;
}
}
}
}
return true;
}
for(let i=0;i<board.length;i+=3) {
for (let j=0;j<board[i].length;j+=3) {
if(!littleReapeat(i,j)) {
return false;
}
}
}
return true;
};
```
# 再看《解数独》解法
第一次的想法就是直接和《有效的数独》解法走反向
1. 先找出横竖和对应该数的九宫格中排除数字,如果只剩下一个结果,则填入
2. 如果存在有结果填入,则再执行第1步。没有则说明没有结果能够判断出来,直接返回
```js
/**
* @param {character[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
var solveSudoku = function(board) {
function checkNowNum(x,y) {
let setMap = new Set(["1","2","3","4","5","6","7","8","9"]);
for(let i=0;i<board[x].length;i++) {
if(board[x][i]!='.') {
setMap.delete(board[x][i])
}
}
for(let i=0;i<board.length;i++) {
if(board[i][y]!='.') {
setMap.delete(board[i][y])
}
}
let startX = Math.floor(x/3)*3;
let startY = Math.floor(y/3)*3;
for(let i = startX;i<startX+3;i++) {
for(let j = startY;j<startY+3;j++) {
if(board[i][j]!='.') {
setMap.delete(board[i][j])
}
}
}
if (setMap.size==1) {
for(let num of setMap) {
return num;
}
} else {
return '.'
}
}
function eachNum() {
let addNum = 0;
for (let i=0;i<board.length;i++) {
for (let j=0;j<board[i].length;j++) {
if (board[i][j]=='.') {
board[i][j] = checkNowNum(i,j);
if (board[i][j]!='.') {
addNum++;
}
}
}
}
if (addNum!=0) {eachNum()};
return board;
}
eachNum();
};
```
一开始我认为上面的解法应该是够了的时候,拿去执行,发现只通过2个测试用例,原来测试用例的数独也不是可以完全判断出来,而是需要碰一些运气。那怎么办呢。
我想到我可以试一下尝试填入。即:
1. 填入无法判断的位置的可能性的数字
2. 根据上面填入的数字进行一步运算,如果不能走通就重制未知的结果,更换可能性数字
那么整体流程就变成了:
1. 先找出横竖和对应该数的九宫格中排除数字,如果只剩下一个结果,则填入
2. 如果存在有结果填入,则再执行第1步。
3. 如果全部计算出,则弹出结果,没有则走下一步
4. 填入无法判断的位置的可能性的数字
5. 再走第一步,如果不能走通就重制未知的结果,更换可能性数字
代码如下:
```js
/**
* @param {character[][]} board
* @return {void}
*/
var solveSudoku = function(board) {
function delUnkownNum (setMap,x,y) {
if(board[x][y]!='.' && typeof board[x][y] =='string') {
setMap.delete(board[x][y])
}
}
function checkNowNum(x,y) {
let setMap = new Set(["1","2","3","4","5","6","7","8","9"]);
for(let i=0;i<board[x].length;i++) {
delUnkownNum (setMap,x,i)
}
for(let i=0;i<board.length;i++) {
delUnkownNum (setMap,i,y)
}
let startX = Math.floor(x/3)*3;
let startY = Math.floor(y/3)*3;
for(let i = startX;i<startX+3;i++) {
for(let j = startY;j<startY+3;j++) {
delUnkownNum (setMap,i,j)
}
}
if (setMap.size==1) {
for(let num of setMap) {
return num;
}
} else {
return setMap;
}
}
function eachNum() {
let addNum = 0;
let unkownNumList = [];
for (let i=0;i<board.length;i++) {
for (let j=0;j<board[i].length;j++) {
if (board[i][j]=='.' || typeof board[i][j] =='object') {
board[i][j] = checkNowNum(i,j);
if (board[i][j]!='.' && typeof board[i][j] =='string') {
addNum++;
} else {
unkownNumList.push({x:i,y:j,num:board[i][j]});
}
}
}
}
if (addNum!=0) {
return eachNum()
} else {
if (unkownNumList.length>0) {
for (let unkownNum of unkownNumList) {
if (unkownNum.num.size==0) {break;}
for(let num of unkownNum.num) {
board[unkownNum.x][unkownNum.y] = num;
for (let unkownNumOther of unkownNumList) {
if (unkownNumOther.y>=unkownNum.y && unkownNumOther.x>=unkownNum.x) {
if (!(unkownNumOther.y==unkownNum.y && unkownNumOther.x==unkownNum.x)) {
board[unkownNumOther.x][unkownNumOther.y] = unkownNumOther.num;
}
}
}
let ukList = eachNum();
if(ukList.length==0) {
return ukList;
} else {
for (let unkownNumOther of unkownNumList) {
if (unkownNumOther.y>=unkownNum.y && unkownNumOther.x>=unkownNum.x) {
board[unkownNumOther.x][unkownNumOther.y] = unkownNumOther.num;
}
}
}
}
}
}
}
return unkownNumList;
}
eachNum();
};
```
通过这次增强运算,意料之中顺利通过了全部用例。
# 总结
先说说我这个最终方案的缺陷吧,我的最终方案其实走了很多的多余运算。如果优化的话,只要发现存在某个位置存在没有任何可能数字的情况下,就应该立刻复盘并更换复盘的可能性数字,而目前是直接算到底,只有发现无法全部算出才进行复盘。这道题,个人认为把`回溯法`表现的十分棒,而`回溯法`的精髓是什么呢?我认为就是`复盘`,这个过程更像是穿越时间回到过去,再把错误的决策给扳正。
================================================
FILE: article/leetcode/sudoku-solver/sudokuSolver.js
================================================
/**
* @param {character[][]} board
* @return {void}
*/
// github@zy445566
var solveSudoku = function(board) {
function delUnkownNum (setMap,x,y) {
if(board[x][y]!='.' && typeof board[x][y] =='string') {
setMap.delete(board[x][y])
}
}
function checkNowNum(x,y) {
let setMap = new Set(["1","2","3","4","5","6","7","8","9"]);
for(let i=0;i<board[x].length;i++) {
delUnkownNum (setMap,x,i)
}
for(let i=0;i<board.length;i++) {
delUnkownNum (setMap,i,y)
}
let startX = Math.floor(x/3)*3;
let startY = Math.floor(y/3)*3;
for(let i = startX;i<startX+3;i++) {
for(let j = startY;j<startY+3;j++) {
delUnkownNum (setMap,i,j)
}
}
if (setMap.size==1) {
for(let num of setMap) {
return num;
}
} else {
return setMap;
}
}
function eachNum() {
let addNum = 0;
let unkownNumList = [];
for (let i=0;i<board.length;i++) {
for (let j=0;j<board[i].length;j++) {
if (board[i][j]=='.' || typeof board[i][j] =='object') {
board[i][j] = checkNowNum(i,j);
if (board[i][j]!='.' && typeof board[i][j] =='string') {
addNum++;
} else {
unkownNumList.push({x:i,y:j,num:board[i][j]});
}
}
}
}
if (addNum!=0) {
return eachNum()
} else {
if (unkownNumList.length>0) {
for (let unkownNum of unkownNumList) {
if (unkownNum.num.size==0) {break;}
for(let num of unkownNum.num) {
board[unkownNum.x][unkownNum.y] = num;
for (let unkownNumOther of unkownNumList) {
if (unkownNumOther.y>=unkownNum.y && unkownNumOther.x>=unkownNum.x) {
if (!(unkownNumOther.y==unkownNum.y && unkownNumOther.x==unkownNum.x)) {
board[unkownNumOther.x][unkownNumOther.y] = unkownNumOther.num;
}
}
}
let ukList = eachNum();
if(ukList.length==0) {
return ukList;
} else {
for (let unkownNumOther of unkownNumList) {
if (unkownNumOther.y>=unkownNum.y && unkownNumOther.x>=unkownNum.x) {
board[unkownNumOther.x][unkownNumOther.y] = unkownNumOther.num;
}
}
}
}
}
}
}
return unkownNumList;
}
eachNum();
};
================================================
FILE: article/leetcode/three-sum/README.md
================================================
# 三数之和的优化之旅
最近一期的996结束,准备在空闲的时间学点什么,就在纠结的时候看到leetcode中国版推出了,正好提升一下自身的算法能力,遂开始刷题。可以说从中有一定提升,但大部分还是暴力刷题过的。这次下班回家刷三数之和的时候感觉纠结了我一晚上,让我吃也吃不下,让我睡也睡失眠,最后在上班路上用手机把这道题做了,我觉得还是有必要记录一下。
# 先说题目
> 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
> 注意:答案中不可以包含重复的三元组。
> 例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
>满足要求的三元组集合为:
```
[
[-1, 0, 1],
[-1, -1, 2]
]
```
# 最早思路,直接暴力刷
#### 当时想法:
1. 直接用三个循环遍历三个数
2. 然后给相加为0的结果排个序,再给这三个数打个map防止重复
#### 根据上面思路走的代码:
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
let threeSumList = [];
let threeSumMap = {};
// 1. 直接用三个循环遍历三个数
for(let i=0;i<nums.length;i++) {
for (let j=i+1;j<nums.length;j++) {
for (let k=j+1;k<nums.length;k++) {
if (nums[i]+nums[j]+nums[k]==0) {
let item = [nums[i],nums[j],nums[k]].sort((a,b)=>{return a-b;});
let itemStr = item.join();
if (!threeSumMap[itemStr]) {
// 2. 然后给相加为0的结果排个序,再给这三个数打个map防止重复
threeSumMap[item.join()] = true;
threeSumList.push(item);
}
}
}
}
}
return threeSumList;
};
```
#### 结果(貌似每个用例时间要小于三秒):
超出时间限制
# 然后思考了一下,试试双指针
#### 当时想法:
1. 先对传入的数组排序
2. 两边指针向中心靠拢
3. 给右指针加上辅指针,向左移
#### 原因:
因为相加要为0,那么让最左边和最右边相加再不断缩小范围不就好了
#### 根据上面思路走的代码:
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
let threeSumList = [];
let threeSumListMap = {};
// 1. 先对传入的数组排序
nums.sort((a,b)=>{return a-b;});
function beatRepeat(num1,num2,num3) {
let res = [num1,num2,num3].sort();
let threeStr = res.join();
if (!threeSumListMap[threeStr]) {
threeSumListMap[threeStr] = true;
threeSumList.push(res)
}
}
// 2. 两边指针向中心靠拢
for (let i=0;i<nums.length;i++) {
for (let j=nums.length-1;j>i+1;j--) {
// 3. 给右指针加上辅指针,向左移
for(let k=j-1;k>i;k--) {
let sumRes = nums[i]+nums[j]+nums[k];
if (sumRes==0) {
beatRepeat(nums[i],nums[j],nums[k])
}
}
}
}
return threeSumList;
};
```
#### 结果(貌似每个用例时间要小于三秒):
超出时间限制
# 开始抓狂,找优化点
#### 当时想法:
1. 不可能,我怎么会失败,肯定是细节没做好
#### 原因:
1. 已经排过序了,那做map的时候可以不用排序了
2. 左右都是0的的可以直接返回了
3. 左边的值和上一个左边值相同说明做过处理了,不需要再处理了
4. 如果最右边的两个值相加没左边的绝对值高,那么是不是右边无论怎么左移都不可能和左边的数相加为0了
5. 如果相加大于0,辅助指针可以直接偏移
6. 如果相加已经开始小于0了,辅助指针可以直接结束本轮了
#### 根据上面思路走的代码:
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
let threeSumList = [];
let threeSumListMap = {};
function beatRepeat(num1,num2,num3) {
// 1. 已经排过序了,那做map的时候可以不用排序了
let res = [num1,num2,num3];
let threeStr = res.join();
if (!threeSumListMap[threeStr]) {
threeSumListMap[threeStr] = true;
threeSumList.push(res)
}
}
nums.sort((a,b)=>{return a-b;});
// 2. 左右都是0的的可以直接返回了
if (nums[0]==0 && nums[nums.length-1]==0) {
if (nums.length>=3) {
return [[0,0,0]]
} else {
return [];
}
}
for (let i=0;i<nums.length,nums[i]<=0;i++) {
// 3. 左边的值和上一个左边值相同说明做过处理了,不需要再处理了
if (i>0) {while(nums[i]==nums[i-1]){i++;}}
for (let j=nums.length-1;j>i+1;j--) {
// 4. 如果最右边的两个值相加没左边的绝对值高,那么是不是右边无论怎么左移都不可能和左边的数相加为0了
if ((nums[i]+nums[j-1]+nums[j])<0){break;}
for (let k=j-1;k>i;k--) {
// 5. 如果相加大于0,辅助指针可以直接偏移
while (nums[i]+nums[k]+nums[j]>0) {k--;}
if (k<=i) {break;}
let sumRes = nums[i]+nums[k]+nums[j];
// 6. 如果相加已经开始小于0了,辅助指针可以直接结束本轮了
if (sumRes<0){break;}
if (sumRes==0) {
beatRepeat(nums[i],nums[k],nums[j])
}
}
}
}
return threeSumList;
};
```
#### 结果(貌似每个用例时间要小于三秒):
超出时间限制(这时心态接近崩溃@ _ @)
# 冷静下来,不空想
#### 排查
使用conosle.count打印了各个循环的计次,当数组大于3000时循环到j和k的次数超过100W次,而且k中还要进行大量运算,如果是100W次有效运算问题应该不大。但k很多都是无效运算。随准备加快k效率。
#### 当时想法:
1. 那能不能实现让辅助指针直接实现跳跃呢
#### 修改点:
##### 原代码:
```js
while (nums[i]+nums[k]+nums[j]>0) {k--;}
```
##### 修改为:
```js
// 折半跳跃
let inc = k-i;
while (nums[i]+nums[k]+nums[j]>0) {
k--;
let zk = k-Math.floor(inc/2);
if (nums[i]+nums[zk]+nums[j]>0) {
k=zk;
}
inc = inc/2;
if (k<=i) {break;}
}
```
#### 根据上面思路修改的代码:
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
let threeSumList = [];
let threeSumListMap = {};
function beatRepeat(num1,num2,num3) {
let res = [num1,num2,num3];
let threeStr = res.join();
if (!threeSumListMap[threeStr]) {
threeSumListMap[threeStr] = true;
threeSumList.push(res)
}
}
nums.sort((a,b)=>{return a-b;});
if (nums[0]==0 && nums[nums.length-1]==0) {
if (nums.length>=3) {
return [[0,0,0]]
} else {
return [];
}
}
for (let i=0;i<nums.length,nums[i]<=0;i++) {
if (i>0) {while(nums[i]==nums[i-1]){i++;}}
for (let j=nums.length-1;j>i+1;j--) {
if ((nums[i]+nums[j-1]+nums[j])<0){break;}
for (let k=j-1;k>i;k--) {
// 辅助指针直接实现跳跃呢
// while (nums[i]+nums[k]+nums[j]>0) {k--;}
let inc = k-i;
while (nums[i]+nums[k]+nums[j]>0) {
k--;
let zk = k-Math.floor(inc/2);
if (nums[i]+nums[zk]+nums[j]>0) {
k=zk;
}
inc = inc/2;
if (k<=i) {break;}
}
if (k<=i) {break;}
let sumRes = nums[i]+nums[k]+nums[j];
if (sumRes<0){break;}
if (sumRes==0) {
beatRepeat(nums[i],nums[k],nums[j])
}
}
}
}
return threeSumList;
};
```
#### 结果:
通过全部用例
# 基准测试结果
```sh
threeSum#1 x 0.05 ops/sec ±5.39% (5 runs sampled)
threeSum#2 x 0.05 ops/sec ±5.05% (5 runs sampled)
threeSum#3 x 0.34 ops/sec ±9.68% (5 runs sampled)
threeSum#4 x 6.97 ops/sec ±2.44% (21 runs sampled)
```
# 结论
threeSum#4执行速度几乎是threeSum#1和threeSum#2的139倍,比threeSum#3也快了接近20倍。
================================================
FILE: article/leetcode/three-sum/package.json
================================================
{
"name": "three-sum",
"version": "1.0.0",
"description": "我的历程",
"main": "treeSumBenchmark.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/zy445566/myBlog.git"
},
"author": "zy445566",
"license": "MIT",
"bugs": {
"url": "https://github.com/zy445566/myBlog/issues"
},
"homepage": "https://github.com/zy445566/myBlog#readme",
"dependencies": {
"benchmark": "^2.1.4"
}
}
================================================
FILE: article/leetcode/three-sum/treeSumBenchmark.js
================================================
var testData= [-99927,-99851,-99822,-99637,-99583,-99555,-99525,-99524,-99508,-99500,-99486,-99468,-99280,-99233,-99156,-98986,-98952,-98929,-98897,-98836,-98810,-98757,-98724,-98715,-98698,-98676,-98647,-98641,-98578,-98421,-98362,-98291,-98256,-98048,-97960,-97822,-97814,-97691,-97680,-97621,-97532,-97516,-97478,-97460,-97425,-97410,-97401,-97397,-97329,-97160,-97159,-97108,-97074,-96987,-96916,-96836,-96770,-96684,-96683,-96610,-96593,-96465,-96321,-96259,-96255,-96252,-96130,-96075,-96005,-95906,-95803,-95785,-95620,-95597,-95422,-94948,-94866,-94855,-94823,-94790,-94619,-94562,-94425,-94163,-94160,-94036,-94030,-93996,-93815,-93703,-93651,-93618,-93616,-93476,-93310,-93252,-93248,-93095,-93081,-92945,-92865,-92851,-92793,-92792,-92757,-92738,-92703,-92681,-92664,-92612,-92588,-92575,-92566,-92523,-92513,-92450,-92393,-92392,-92390,-92337,-92254,-92230,-92145,-91994,-91953,-91947,-91939,-91907,-91865,-91859,-91813,-91763,-91704,-91619,-91600,-91540,-91493,-91438,-91369,-91184,-91120,-91003,-90996,-90969,-90933,-90887,-90885,-90794,-90724,-90694,-90620,-90612,-90610,-90606,-90553,-90526,-90452,-90415,-90402,-90358,-90331,-90240,-90106,-89959,-89834,-89755,-89753,-89730,-89694,-89667,-89526,-89457,-89440,-89435,-89382,-89304,-89296,-89260,-89210,-89156,-89086,-89035,-89010,-88925,-88916,-88882,-88868,-88807,-88743,-88701,-88681,-88663,-88627,-88497,-88412,-88408,-88303,-88164,-88104,-88080,-88048,-87852,-87787,-87783,-87672,-87650,-87645,-87498,-87445,-87359,-87230,-86940,-86931,-86874,-86870,-86804,-86650,-86647,-86464,-86454,-86430,-86269,-86218,-86121,-86040,-85965,-85873,-85789,-85771,-85755,-85686,-85617,-85560,-85530,-85523,-85473,-85355,-85262,-85247,-85164,-84981,-84883,-84838,-84664,-84625,-84589,-84360,-84303,-84217,-84207,-84128,-84113,-84083,-83966,-83947,-83940,-83916,-83896,-83841,-83831,-83805,-83710,-83664,-83631,-83616,-83602,-83570,-83518,-83514,-83486,-83459,-83447,-83303,-83216,-83216,-83215,-83202,-83092,-83086,-83082,-83060,-83057,-82946,-82944,-82943,-82932,-82869,-82800,-82793,-82739,-82715,-82612,-82536,-82493,-82492,-82413,-82405,-82319,-82289,-82153,-82082,-82045,-81923,-81875,-81810,-81683,-81611,-81607,-81590,-81446,-81400,-81313,-81271,-81264,-81253,-81168,-81153,-81142,-81117,-80932,-80844,-80806,-80784,-80728,-80585,-80578,-80456,-80418,-80364,-80301,-80293,-80287,-80113,-80085,-79793,-79741,-79661,-79621,-79617,-79568,-79450,-79254,-79173,-79129,-79095,-79040,-79023,-78947,-78842,-78764,-78629,-78586,-78498,-78464,-78376,-78253,-78226,-78219,-78214,-78147,-78147,-78018,-77963,-77907,-77734,-77610,-77577,-77551,-77370,-77356,-77338,-77259,-77256,-77138,-76906,-76897,-76870,-76842,-76756,-76732,-76718,-76705,-76582,-76493,-76459,-76422,-76418,-76274,-76219,-76102,-76093,-76090,-76069,-76034,-76021,-75899,-75794,-75791,-75666,-75555,-75464,-75359,-75305,-75289,-75237,-75232,-75189,-75131,-75116,-75072,-74996,-74972,-74955,-74936,-74818,-74801,-74728,-74680,-74582,-74579,-74308,-74291,-74290,-74260,-74205,-74166,-74041,-73930,-73927,-73885,-73805,-73756,-73723,-73584,-73557,-73495,-73471,-73440,-73422,-73263,-73070,-72973,-72965,-72953,-72860,-72750,-72723,-72702,-72516,-72330,-72226,-72223,-72207,-72117,-72106,-72082,-72080,-72047,-71914,-71848,-71642,-71622,-71595,-71591,-71565,-71560,-71541,-71440,-71273,-71266,-71232,-71078,-71059,-71037,-70957,-70922,-70919,-70833,-70825,-70819,-70802,-70758,-70753,-70514,-70169,-70134,-70076,-70041,-70032,-70024,-69926,-69897,-69828,-69708,-69663,-69486,-69453,-69442,-69420,-69337,-69333,-69329,-69321,-69289,-69204,-69164,-69147,-69074,-69062,-69050,-69049,-69008,-68955,-68915,-68813,-68603,-68544,-68487,-68477,-68441,-68411,-68331,-68177,-68171,-68169,-68101,-68031,-68025,-67949,-67876,-67811,-67719,-67698,-67683,-67663,-67612,-67574,-67542,-67471,-67321,-67306,-67280,-67261,-67214,-67208,-67129,-67101,-66752,-66658,-66630,-66627,-66616,-66578,-66491,-66472,-66447,-66424,-66403,-66330,-66294,-66080,-65995,-65727,-65688,-65644,-65604,-65560,-65332,-65195,-65130,-65021,-64983,-64919,-64727,-64704,-64536,-64427,-64366,-64259,-64125,-64088,-64040,-64026,-63959,-63844,-63767,-63742,-63725,-63710,-63694,-63685,-63647,-63625,-63625,-63598,-63544,-63541,-63492,-63466,-63436,-63412,-63318,-63081,-63032,-63012,-62995,-62659,-62594,-62542,-62429,-62403,-62360,-62332,-62316,-62218,-62209,-62203,-62050,-61995,-61986,-61941,-61937,-61838,-61823,-61761,-61595,-61590,-61575,-61569,-61523,-61520,-61492,-61395,-61382,-61322,-61188,-61162,-61090,-61011,-60781,-60750,-60620,-60556,-60526,-60489,-60418,-60383,-60271,-60228,-60202,-60191,-60074,-60070,-60043,-60042,-59914,-59758,-59741,-59737,-59677,-59560,-59515,-59469,-59461,-59390,-59342,-59174,-59130,-59103,-59094,-58893,-58870,-58810,-58628,-58602,-58524,-58462,-58451,-58451,-58440,-58425,-58372,-58341,-58305,-58293,-58272,-58148,-58118,-57996,-57876,-57730,-57658,-57613,-57607,-57553,-57553,-57513,-57347,-57340,-57313,-57301,-57185,-56939,-56753,-56695,-56679,-56678,-56663,-56625,-56510,-56469,-56120,-56103,-56062,-56061,-56048,-56022,-55790,-55692,-55543,-55480,-55428,-55401,-55332,-55171,-55107,-54944,-54931,-54714,-54710,-54696,-54577,-54564,-54534,-54458,-54452,-54439,-54252,-54126,-53985,-53707,-53678,-53580,-53451,-53447,-53306,-53238,-53190,-53178,-53156,-53067,-52866,-52803,-52764,-52579,-52565,-52507,-52480,-52477,-52459,-52407,-52376,-52376,-52318,-52263,-52250,-52197,-52108,-52097,-52075,-52056,-52038,-51965,-51952,-51791,-51721,-51700,-51616,-51478,-51439,-51333,-51280,-51185,-51149,-51098,-50881,-50879,-50811,-50749,-50708,-50585,-50566,-50451,-50441,-50426,-50416,-50276,-50122,-50022,-49979,-49912,-49851,-49712,-49656,-49489,-49482,-49430,-49426,-49405,-49344,-49337,-49127,-48949,-48935,-48904,-48864,-48745,-48716,-48676,-48568,-48542,-48480,-48444,-48333,-48285,-48140,-48004,-47921,-47890,-47763,-47670,-47605,-47578,-47492,-47430,-47425,-47352,-47239,-47104,-47100,-47060,-47009,-46941,-46883,-46828,-46809,-46761,-46721,-46692,-46602,-46597,-46596,-46492,-46477,-46417,-46399,-46334,-46254,-46131,-46107,-46053,-46016,-45943,-45750,-45606,-45586,-45575,-45504,-45504,-45484,-45416,-45341,-45133,-45030,-44956,-44888,-44878,-44719,-44684,-44679,-44516,-44508,-44371,-44319,-44099,-44016,-44011,-43981,-43776,-43667,-43663,-43630,-43492,-43485,-43428,-43348,-43306,-43302,-43051,-42977,-42960,-42946,-42889,-42743,-42659,-42536,-42426,-42421,-42362,-42315,-42278,-42251,-42137,-41962,-41842,-41814,-41689,-41677,-41668,-41661,-41622,-41578,-41571,-41526,-41525,-41233,-41144,-40908,-40785,-40586,-40573,-40562,-40524,-40490,-40453,-40357,-40278,-40199,-40180,-40161,-40127,-40072,-40007,-39968,-39902,-39897,-39831,-39736,-39585,-39568,-39454,-39454,-39394,-39308,-39095,-39050,-38987,-38820,-38786,-38763,-38739,-38677,-38586,-38533,-38446,-38196,-38077,-38057,-37731,-37720,-37656,-37653,-37597,-37375,-37263,-37210,-37043,-36895,-36893,-36864,-36858,-36821,-36775,-36714,-36675,-36601,-36576,-36551,-36529,-36459,-36455,-36392,-36333,-36315,-36232,-36197,-36143,-36046,-35794,-35731,-35688,-35464,-35441,-35391,-35249,-35139,-35117,-35093,-35033,-35030,-34949,-34916,-34855,-34821,-34819,-34789,-34689,-34686,-34678,-34637,-34573,-34503,-34499,-34410,-34333,-34231,-34168,-34162,-34049,-33924,-33920,-33854,-33834,-33612,-33532,-33505,-33492,-33295,-33188,-33009,-32971,-32958,-32884,-32847,-32754,-32727,-32576,-32518,-32397,-32293,-32245,-32197,-32119,-31992,-31933,-31918,-31874,-31774,-31757,-31721,-31654,-31620,-31466,-31423,-31422,-31358,-31274,-30883,-30868,-30796,-30706,-30669,-30585,-30439,-30248,-30195,-30186,-30105,-30093,-30048,-30027,-29756,-29694,-29515,-29470,-29420,-29393,-29370,-29364,-29255,-29214,-29102,-29078,-29073,-29066,-29033,-28976,-28937,-28801,-28750,-28719,-28697,-28689,-28680,-28665,-28645,-28457,-28396,-28299,-28279,-28048,-28037,-27977,-27935,-27826,-27790,-27763,-27760,-27688,-27669,-27644,-27523,-27472,-27460,-27445,-27394,-27177,-27101,-27054,-27028,-27012,-26988,-26965,-26930,-26665,-26644,-26621,-26573,-26521,-26503,-26266,-26265,-26173,-26104,-26056,-25921,-25874,-25821,-25779,-25727,-25584,-25571,-25344,-25277,-25222,-25120,-25117,-25069,-25069,-25059,-25033,-24978,-24970,-24958,-24944,-24921,-24919,-24890,-24889,-24871,-24808,-24761,-24751,-24692,-24662,-24568,-24459,-24404,-24375,-24229,-24150,-24077,-24066,-23992,-23904,-23867,-23867,-23844,-23708,-23689,-23640,-23604,-23447,-23394,-23386,-23385,-23110,-23037,-22953,-22854,-22797,-22763,-22748,-22684,-22556,-22380,-22352,-22308,-22293,-22272,-22258,-22165,-22073,-22016,-21856,-21781,-21642,-21612,-21570,-21545,-21498,-21405,-21383,-21156,-21108,-21052,-20888,-20716,-20655,-20572,-20564,-20389,-20289,-20267,-20216,-20060,-19921,-19644,-19607,-19475,-19410,-19391,-19370,-19357,-19335,-19246,-19177,-19165,-19145,-19082,-18980,-18942,-18874,-18838,-18791,-18747,-18563,-18547,-18461,-18422,-18348,-18331,-18242,-18221,-18211,-18159,-18126,-18080,-17902,-17790,-17561,-17452,-17365,-17363,-17341,-17223,-17025,-17008,-16929,-16922,-16866,-16832,-16760,-16580,-16523,-16511,-16464,-16350,-16324,-16267,-16151,-16109,-16085,-16065,-16000,-15982,-15917,-15816,-15802,-15741,-15659,-15653,-15619,-15524,-15501,-15477,-15462,-15331,-15317,-15293,-15239,-15206,-15121,-15114,-15086,-15052,-15016,-14915,-14866,-14859,-14782,-14778,-14606,-14600,-14531,-14530,-14452,-14388,-14270,-14263,-14258,-14104,-14087,-14003,-13897,-13840,-13782,-13726,-13706,-13658,-13631,-13594,-13547,-13505,-13500,-13439,-13417,-13304,-13174,-13155,-13016,-12957,-12879,-12761,-12716,-12641,-12609,-12470,-12458,-12393,-12388,-12374,-12235,-12228,-12179,-12113,-12041,-11931,-11829,-11753,-11670,-11662,-11555,-11433,-11318,-11293,-11270,-11261,-11242,-11219,-11113,-10960,-10957,-10859,-10696,-10660,-10640,-10400,-10301,-10238,-10218,-10184,-10130,-10101,-9741,-9732,-9642,-9474,-9461,-9310,-9274,-9243,-9241,-9096,-9037,-9022,-8966,-8938,-8853,-8849,-8838,-8832,-8824,-8801,-8798,-8763,-8682,-8527,-8420,-8385,-8270,-8217,-7908,-7877,-7845,-7810,-7679,-7642,-7628,-7617,-7611,-7535,-7532,-7528,-7418,-7341,-7269,-7240,-7180,-7049,-7041,-6954,-6939,-6864,-6601,-6511,-6493,-6468,-6417,-6242,-6218,-6167,-6088,-5933,-5860,-5589,-5532,-5475,-5475,-5453,-5431,-5306,-5021,-4942,-4913,-4899,-4866,-4423,-4322,-4259,-4257,-4185,-4159,-4105,-4088,-4026,-3990,-3765,-3728,-3642,-3596,-3555,-3490,-3487,-3406,-3333,-3303,-3271,-3251,-3221,-3218,-3046,-2989,-2954,-2951,-2919,-2863,-2707,-2586,-2460,-2445,-2437,-2388,-2314,-2288,-2243,-2156,-2151,-2141,-2072,-1958,-1900,-1859,-1806,-1760,-1741,-1636,-1560,-1523,-1483,-1415,-1408,-1343,-1331,-1187,-1186,-1119,-759,-602,-585,-562,-542,-447,-394,-104,-90,20,21,46,250,252,324,727,792,860,874,910,910,925,929,1202,1209,1454,1558,1591,1637,1897,1909,1919,1988,2099,2246,2344,2391,2590,2689,2705,2759,2803,2826,2890,2909,3015,3023,3063,3114,3213,3295,3302,3325,3340,3342,3638,3649,3649,3695,3848,3900,3939,4031,4071,4103,4247,4280,4320,4394,4460,4508,4656,4675,4713,4856,5017,5262,5355,5356,5453,5465,5467,5475,5680,5852,5897,5992,6001,6019,6059,6086,6184,6214,6364,6421,6428,6739,6822,6912,7170,7225,7228,7252,7332,7344,7352,7461,7712,8284,8315,8434,8623,8731,8838,8845,8860,8940,8942,8959,8990,9104,9147,9174,9255,9337,9344,9344,9541,9612,9623,9883,9947,9948,10029,10052,10079,10093,10142,10151,10153,10159,10236,10390,10406,10481,10673,10709,10710,11092,11219,11276,11422,11474,11515,11576,11721,11839,11855,12052,12200,12241,12326,12333,12582,12642,12687,12718,12741,12783,12808,12820,12900,13033,13041,13042,13048,13091,13141,13170,13200,13211,13213,13343,13483,13501,13606,13615,13616,13621,13686,13700,13777,13805,13848,13859,13947,13974,14014,14079,14162,14184,14198,14333,14349,14367,14525,14627,14740,14848,14861,14906,14961,14996,15016,15086,15129,15396,15473,15498,15686,15690,15750,15779,15922,15929,15966,15970,16059,16244,16296,16538,16590,16634,16659,16814,16822,17016,17056,17165,17307,17318,17452,17642,17708,17718,17764,17910,17921,17984,17994,18008,18041,18096,18123,18179,18242,18403,18470,18516,18593,18669,18683,18749,18766,18913,19121,19343,19355,19355,19369,19450,19486,19491,19546,19553,19664,19672,19700,19746,19758,19878,19927,19956,19997,20017,20057,20123,20310,20356,20472,20496,20497,20515,20577,20646,20757,20801,20811,20868,20937,20997,21034,21111,21122,21274,21276,21291,21361,21395,21412,21426,21515,21566,21638,21652,21692,21710,21710,21779,21781,21874,21960,21971,22001,22002,22062,22319,22422,22451,22452,22481,22755,22843,22903,23108,23271,23319,23330,23414,23470,23552,23553,23618,23670,23772,23913,24007,24079,24100,24112,24179,24197,24208,24296,24406,24422,24514,24520,24574,24597,24626,24666,24669,24717,24873,24979,24986,25009,25014,25043,25057,25131,25160,25209,25227,25309,25327,25610,25689,25724,25731,25742,25797,25874,26003,26276,26281,26339,26377,26397,26451,26585,26714,26825,26862,26898,26905,26942,26956,27036,27054,27100,27134,27203,27308,27518,27632,27697,27749,27778,27821,27979,28001,28007,28009,28095,28134,28149,28152,28160,28301,28483,28501,28613,28632,28696,28699,28794,28846,28924,28971,29035,29119,29174,29239,29265,29467,29471,29546,29608,29754,29755,29853,29914,29948,30008,30113,30159,30165,30218,30254,30414,30513,30528,30925,30952,30997,31015,31087,31295,31349,31495,31539,31570,31612,31621,31640,31658,31679,31796,31953,31964,32026,32075,32083,32138,32165,32289,32329,32429,32506,32564,32565,32622,32687,32742,32756,32867,32900,32976,33005,33033,33127,33132,33298,33316,33407,33447,33483,33489,33496,33537,33590,33636,33665,33684,33686,33701,33882,33928,33950,33994,34037,34049,34098,34102,34153,34251,34311,34312,34323,34328,34331,34348,34381,34386,34473,34557,34660,34878,34911,34932,35008,35009,35041,35111,35225,35241,35243,35347,35368,35369,35440,35502,35528,35659,35673,35805,35845,35857,35939,36028,36095,36152,36154,36172,36255,36398,36863,36946,36952,36985,37145,37154,37179,37202,37208,37236,37280,37296,37446,37578,37633,37657,37675,37766,37814,37848,37906,37940,38004,38160,38269,38342,38375,38528,38615,38736,38739,38773,38917,39086,39105,39147,39189,39209,39217,39225,39235,39284,39328,39331,39345,39356,39417,39522,39560,39576,39701,39726,39730,39745,39777,39795,39948,40223,40311,40327,40398,40517,40551,40582,40594,40623,40680,40699,40830,40887,40887,40969,40989,41014,41206,41206,41326,41412,41488,41488,41515,41702,41704,41728,41728,41779,41791,41837,42020,42058,42117,42151,42184,42264,42275,42328,42342,42518,42668,42702,42833,43003,43136,43242,43338,43450,43457,43564,43645,43767,43770,43994,44047,44119,44240,44249,44254,44310,44466,44484,44611,44660,44727,44749,44774,44808,44850,45192,45238,45328,45413,45595,45735,45869,45871,45941,46048,46085,46091,46119,46141,46192,46221,46269,46344,46421,46457,46475,46482,46491,46545,46603,46691,46773,46811,46887,46918,46991,46999,47124,47227,47271,47342,47389,47456,47678,47703,47778,47999,48010,48049,48050,48137,48189,48302,48355,48452,48471,48569,48599,48720,48773,48779,48990,49088,49097,49098,49235,49277,49542,49707,49900,50061,50098,50360,50412,50495,50497,50523,50544,50584,50661,50668,50742,50777,50845,50846,50876,50911,50932,50964,51023,51055,51443,51476,51611,51644,51717,51801,51875,51908,52061,52088,52109,52161,52162,52174,52208,52236,52307,52461,52493,52533,52820,53049,53073,53199,53262,53313,53417,53554,53611,53623,53700,53741,53852,53901,54011,54029,54122,54289,54369,54603,54739,54856,54882,54953,54955,54958,55146,55172,55261,55276,55328,55333,55349,55379,55412,55439,55669,55752,55754,55817,55836,55858,55946,55952,55956,56024,56067,56308,56512,56530,56541,56558,56740,56795,56806,56965,57045,57333,57377,57711,57729,57745,57745,57879,57896,57915,57991,58181,58272,58280,58296,58449,58486,58678,58687,58733,58781,58850,58860,58884,58964,59004,59052,59178,59247,59405,59527,59557,59560,59669,59697,59818,59883,59902,59977,60115,60125,60369,60370,60406,60429,60450,60489,60546,60560,60630,60842,60969,61047,61557,61708,61731,61805,61808,61905,61910,61932,62042,62069,62082,62082,62101,62254,62283,62339,62346,62390,62404,62417,62458,62481,62535,62593,62628,62667,62937,63075,63085,63093,63112,63115,63199,63260,63289,63318,63340,63342,63680,63692,63721,63727,63836,63850,63915,63918,63969,64017,64255,64255,64257,64387,64460,64481,64483,64484,64502,64607,64610,64655,64681,64713,64714,64803,64824,64893,64925,64935,65167,65213,65216,65258,65260,65265,65308,65414,65453,65474,65698,65799,65838,65862,65886,65892,65903,66073,66103,66158,66165,66287,66451,66452,66480,66543,66564,66691,66834,66941,67013,67030,67039,67131,67131,67280,67294,67323,67357,67384,67417,67457,67576,67581,67697,67731,67780,67846,67873,67981,68060,68092,68206,68232,68331,68379,68445,68449,68472,68480,68487,68498,68510,68518,68554,68602,68617,68684,68706,68767,68834,68863,68882,68937,68942,69060,69150,69188,69222,69243,69291,69357,69512,69600,69735,69743,69964,69970,70155,70168,70275,70331,70423,70458,70506,70550,70628,70736,70858,70894,70957,70963,70996,71128,71164,71199,71258,71317,71396,71443,71458,71470,71527,71756,71787,71815,71847,71900,72088,72100,72135,72419,72793,72855,72935,72998,73172,73180,73414,73415,73421,73486,73500,73635,73688,73875,73878,73904,74005,74012,74042,74079,74193,74197,74299,74308,74337,74436,74438,74454,74537,74685,74690,74716,74746,74913,74944,74977,75064,75135,75318,75456,75538,75579,75712,75719,75755,75860,75973,75987,76024,76104,76370,76496,76522,76530,76538,76678,76713,76713,76852,77018,77036,77038,77104,77169,77372,77451,77462,77626,77655,77671,77831,77900,77916,77975,77999,78013,78028,78039,78051,78072,78242,78266,78391,78409,78439,78775,78867,78906,78939,78998,79058,79110,79462,79542,79616,79630,79713,79715,79907,79952,80002,80020,80021,80060,80157,80183,80358,80385,80429,80499,80545,80917,80934,80990,80991,81048,81119,81194,81310,81311,81399,81515,81543,81587,81627,81649,81712,81936,81936,82052,82093,82255,82342,82411,82540,82597,82620,82659,82662,82664,82874,82908,82975,82982,83030,83172,83192,83193,83272,83390,83436,83440,83529,83735,83784,83794,83889,83996,84018,84134,84290,84387,84524,84651,84659,84870,84902,84912,84935,85003,85040,85066,85274,85354,85395,85425,85442,85484,85501,85547,85680,85742,85818,85850,85950,85995,86035,86069,86168,86272,86362,86368,86440,86522,86547,86622,86631,86643,86744,86859,86893,86941,87061,87081,87240,87338,87429,87480,87492,87579,87660,88235,88316,88361,88377,88410,88467,88499,88511,88533,88535,88652,88660,88678,88741,88781,88785,89012,89093,89334,89404,89614,89650,89707,89717,89758,89870,89892,89906,89912,89984,90206,90324,90439,90476,90535,90592,90745,90840,90990,91033,91075,91207,91213,91225,91276,91365,91464,91515,91826,92029,92095,92154,92159,92186,92263,92511,92529,92606,92647,92761,92761,92792,92906,92971,93001,93075,93123,93149,93255,93322,93340,93383,93410,93448,93501,93503,93619,93656,93702,94023,94045,94065,94175,94197,94249,94260,94273,94326,94342,94377,94435,94675,94757,94825,95040,95129,95471,95509,95716,95781,95817,95859,95993,96041,96242,96340,96459,96588,96727,96810,96842,96906,96922,96931,96992,97077,97082,97088,97212,97412,97427,97446,97456,97457,97476,97528,97556,97628,97630,97681,97832,98044,98052,98088,98158,98175,98234,98236,98258,98422,98424,98457,98535,98562,98612,98640,98660,98711,98801,98892,98899,98905,98955,98994,98995,99021,99083,99093,99159,99197,99308,99494,99537,99559,99585,99587,99634,99664,99801,99818,99905,99918]
var threeSum1 = function(nums) {
let threeSumList = [];
let threeSumMap = {};
// 1. 直接用三个循环遍历三个数
for(let i=0;i<nums.length;i++) {
for (let j=i+1;j<nums.length;j++) {
for (let k=j+1;k<nums.length;k++) {
if (nums[i]+nums[j]+nums[k]==0) {
let item = [nums[i],nums[j],nums[k]].sort((a,b)=>{return a-b;});
let itemStr = item.join();
if (!threeSumMap[itemStr]) {
// 2. 然后给相加为0的结果排个序,再给这三个数打个map防止重复
threeSumMap[item.join()] = true;
threeSumList.push(item);
}
}
}
}
}
return threeSumList;
};
var threeSum2 = function(nums) {
let threeSumList = [];
let threeSumListMap = {};
// 1. 先对传入的数组排序
nums.sort((a,b)=>{return a-b;});
function beatRepeat(num1,num2,num3) {
let res = [num1,num2,num3].sort();
let threeStr = res.join();
if (!threeSumListMap[threeStr]) {
threeSumListMap[threeStr] = true;
threeSumList.push(res)
}
}
// 2. 两边指针向中心靠拢
for (let i=0;i<nums.length;i++) {
for (let j=nums.length-1;j>i+1;j--) {
// 3. 给右指针加上辅指针,向左移
for(let k=j-1;k>i;k--) {
let sumRes = nums[i]+nums[j]+nums[k];
if (sumRes==0) {
beatRepeat(nums[i],nums[j],nums[k])
}
}
}
}
return threeSumList;
};
var threeSum3 = function(nums) {
let threeSumList = [];
let threeSumListMap = {};
function beatRepeat(num1,num2,num3) {
// 1. 已经排过序了,那做map的时候可以不用排序了
let res = [num1,num2,num3];
let threeStr = res.join();
if (!threeSumListMap[threeStr]) {
threeSumListMap[threeStr] = true;
threeSumList.push(res)
}
}
nums.sort((a,b)=>{return a-b;});
// 2. 左右都是0的的可以直接返回了
if (nums[0]==0 && nums[nums.length-1]==0) {
if (nums.length>=3) {
return [[0,0,0]]
} else {
return [];
}
}
for (let i=0;i<nums.length,nums[i]<=0;i++) {
// 3. 左边的值和上一个左边值相同说明做过处理了,不需要再处理了
if (i>0) {while(nums[i]==nums[i-1]){i++;}}
for (let j=nums.length-1;j>i+1;j--) {
// 4. 如果最右边的两个值相加没左边的绝对值高,那么是不是右边无论怎么左移都不可能和左边的数相加为0了
if ((nums[i]+nums[j-1]+nums[j])<0){break;}
for (let k=j-1;k>i;k--) {
// 5. 如果相加大于0,辅助指针可以直接偏移
while (nums[i]+nums[k]+nums[j]>0) {k--;}
if (k<=i) {break;}
let sumRes = nums[i]+nums[k]+nums[j];
// 6. 如果相加已经开始小于0了,辅助指针可以直接结束本轮了
if (sumRes<0){break;}
if (sumRes==0) {
beatRepeat(nums[i],nums[k],nums[j])
}
}
}
}
return threeSumList;
};
var threeSum4 = function(nums) {
let threeSumList = [];
let threeSumListMap = {};
function beatRepeat(num1,num2,num3) {
let res = [num1,num2,num3];
let threeStr = res.join();
if (!threeSumListMap[threeStr]) {
threeSumListMap[threeStr] = true;
threeSumList.push(res)
}
}
nums.sort((a,b)=>{return a-b;});
if (nums[0]==0 && nums[nums.length-1]==0) {
if (nums.length>=3) {
return [[0,0,0]]
} else {
return [];
}
}
for (let i=0;i<nums.length,nums[i]<=0;i++) {
if (i>0) {while(nums[i]==nums[i-1]){i++;}}
for (let j=nums.length-1;j>i+1;j--) {
if ((nums[i]+nums[j-1]+nums[j])<0){break;}
for (let k=j-1;k>i;k--) {
// 辅助指针直接实现跳跃呢
// while (nums[i]+nums[k]+nums[j]>0) {k--;}
let inc = k-i;
while (nums[i]+nums[k]+nums[j]>0) {
k--;
let zk = k-Math.floor(inc/2);
if (nums[i]+nums[zk]+nums[j]>0) {
k=zk;
}
inc = inc/2;
if (k<=i) {break;}
}
if (k<=i) {break;}
let sumRes = nums[i]+nums[k]+nums[j];
if (sumRes<0){break;}
if (sumRes==0) {
beatRepeat(nums[i],nums[k],nums[j])
}
}
}
}
return threeSumList;
};
const benchmark = require('benchmark');
let suit = new benchmark.Suite();
suit.add('threeSum#1',function(){
threeSum1(testData);
}).add('threeSum#2',function(){
threeSum2(testData);
}).add('threeSum#3',function(){
threeSum3(testData);
}).add('threeSum#4',function(){
threeSum4(testData);
}).on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({ 'async': true });
================================================
FILE: article/linux/README.md
================================================
# linux
* [教你一步一步利用rsyslog搭建自己的日志系统(20170808)](../linux/rsyslog/README.md)
* [警惕Linux发行版危机和即将或已经发生的重大影响(20230221)](../linux/centos-danger/README.md)
================================================
FILE: article/linux/centos-danger/README.md
================================================
# 警惕Linux发行版危机和即将或已经发生的重大影响
起因是Node.js的开发者提出要升级Node.js镜像版本,原因是Node.js16由于OpenSSL 1.1.1停止维护即将于今年9月份提前停止维护,具体可参考[Node.js16停止维护声明](https://nodejs.org/en/blog/announcements/nodejs16-eol/),也就意味着我们至少要着手准备Node.js18及以上的镜像了。本以为升个版本也就是改个版本号的事情,但接下来发生的事情足以摧毁一个公司的基建系统。
# 0x1 祸起,事情不是那么简单
由于大部分公司的系统都是基于CentOS,我司也是一样,所以我这边首先通过Node.js官网下载最新的Node.js18的CentOS的二进制包,同时打入镜像进行构建,一切都非常顺利,原以为和16一样改个版本号就结束工作,但是直到运行node后,却发生这样的报错。
```sh
sh-4.2# node
node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by node)
node: /lib64/libc.so.6: version `GLIBC_2.25' not found (required by node)
node: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by node)
node: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by node)
node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by node)
node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by node)
```
也就是说C++库版本不对,赶紧查看一下GLIBC版本
```sh
sh-4.2# ldd --version
ldd (GNU libc) 2.17
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
```
这时候首先做的是使用使用yum升级版本,发现这已经是目前最高可用版本。
也就意味着CentOS7,已经无法自动升级,也就意味着只有手动升级GLIBC版本,如果不使用SCL,了解的同学就知道这意味着什么,如果成功无疑等价于你实现了CentOS7的一个新的发行版!
单独升级GLIBC里面很多坑,升级了会导致很多依赖不兼容。
再看来一眼Node.js的issue很早有人开始提出恢复LTS版本在CentOS7的构建,[点击查看](https://github.com/nodejs/node/issues/43246),但貌似官方也没有一个肯定的回复,就目前的结果来看,并没有恢复。
或许这就是最早的蝴蝶开始煽动翅膀了,危机开始扩散。
# 0x2 尝试升级CentOS
那基本只剩一条路直接升级系统,首先升级到了CentOS8发现,yum直接作废。
也就是说CentOS8提供的yum服务直接关闭,看了一眼[官方公告(点击可查看)](https://www.centos.org/centos-linux-eol/),才知道CentOS8在2021年12月31日就已经停止了维护。
取而代之的是Stream版本,但是这个版本就相当于是Red Hat的beta版本,换句话来说新版本的坑先让CentOS的Stream先趟一遍,这还不算,哪怕你说服了自己,算了升级Stream版本,Docker也不给你机会了。[笑哭]
在docker的[CentOS官方镜像详情(点击可查看)](https://hub.docker.com/_/centos)中的开头就有下面一段话。
```
This image is no longer supported/maintained (non-EOL tags last updated November 16, 2020, docker-library/official-images#9102; see also https://www.centos.org/centos-linux-eol/ and docker-library/docs#2205). Please adjust your usage accordingly.
```
什么意思呢?简单来说就是docker的官方CentOS镜像作废。
这意味着什么?
这已经不是风险开始萌芽了,而是风险即将爆发了!
目前来看最优解只能缓慢从CentOS切换到其它的Linux发行版上。而切换到另一个Linux发行版很多基于CentOS镜像中的脚本几乎要全部进行迁移,这无疑是一个大的工作量,但我们却又不得不做,因为这是一个真正的系统性风险,一旦风险彻底暴露,等价于你裸体站在所有人面前。
# 0x3 如何选择
去掉收费的Linux系统不考虑,目前来看Debian和rockylinux,都是主要可选项。
缺点是rockylinux推出的时间不是很长,Debian的话需要迁移大量初始化脚本和代码。
如果出于成本考虑选rockylinux,如果是出于长期性考虑,我选Debian因为品牌久远一点同时几乎没有撂挑子的可能性,如果撂挑子了身为高知名度的ubuntu几乎可以平滑接盘,而rockylinux接盘侠就只能回到最初的起点了。
# 0x4 最后的警报
其实很早CentOS的停止维护危机就已经出现,但是之前没有产生实际的影响,但是目前CentOS7的停止维护日期即将到来,已经开始产生切实的影响了,我觉得是时候拉响警报了。
这是一个技术人在风暴来临前发起的警报!请注意!请注意!请注意!
================================================
FILE: article/linux/rsyslog/README.md
================================================
# 教你一步一步利用rsyslog搭建自己的日志系统
## rsys是什么
rsyslog 是一个syslog的升级版,可用于处理大型的系统的日志需求。并且可以把输入到日志转换到各个数据系统上,如图。

## 教程开始
本教材使用centos系统,其他系统只能作为参考作用
## 如何安装
一般来说centos都自带了rsyslog,当然你也可以通过源码包来安装 <br />
源码包下载地址:http://www.rsyslog.com/downloads/download-v8-stable/ <br />
或使用 <br />
```sh
sudo yum install rsyslog
```
这里就不再赘述,相信这一步很快能度过
## 如何开启我们的rsyslog远程服务
由于我们要远程写入日志,所以我们要打开支持远程的连接,使用vim打开文件
```sh
sudo vim /etc/rsyslog.conf
```
并将以下注释打开
```ini
# Provides UDP syslog reception
$ModLoad imudp #打开udp模块
$UDPServerRun 514 #打开udp服务,并监听514端口
# Provides TCP syslog reception
$ModLoad imtcp #打开tcp模块
$InputTCPServerRun 514 #打开tcp服务,并监听514端口
```
然后重启我们的服务
```sh
sudo service rsyslog restart
```
## 如何使用我们的node进行连接来写入日志
其实这一步很简单,根据上面开启的服务,直接使用原生的socket即可<br />
直接上代码(下面两种版本选一种即可,udp快,但不能保证传输必达性)<br />
```js
//tcp版本
const net = require('net');
function writeLog(port,host,logInfo)
{
var client = new net.Socket();
return new Promise((resolve,reject)=>{
client.connect(port, host, () => {
client.end(logInfo);
client.destroy();
resolve(true);
});
client.once('error', (err) => {
console.log(err);
reject(err);
});
});
}
writeLog(514,'127.0.0.1','hello!rsyslog!');
```
```js
//udp版本
const dgram = require('dgram');
function writeLog(port,host,logInfo)
{
var client = dgram.createSocket('udp4');
return new Promise((resolve,reject)=>{
var message = Buffer.from(logInfo);
client.send(message, port, host, (err) => {
if(err)reject(err);
resolve(true);
client.close();
});
});
}
```
这时我们如何查看信息呢?很简单,默认是在/var/log/message文件中<br />
```
tail -f /var/log/message
```
我们应该可以在末端看到“hello!rsyslog!”<br />
但所有文件都在message,我存其他地方行不行?<br />
这就要涉及rsyslog规则了<br />
### 如何使用rsyslog规则
在配置文件/etc/rsyslog.conf里面,我们能看到默认规则
```ini
#### RULES ####
# Log all kernel messages to the console.
# Logging much else clutters up the screen.
#kern.* /dev/console
# Log anything (except mail) of level info or higher.
# Don't log private authentication messages!
*.info;mail.none;authpriv.none;cron.none /var/log/messages
# The authpriv file has restricted access.
authpriv.* /var/log/secure
# Log all the mail messages in one place.
mail.* -/var/log/maillog
# Log cron stuff
cron.* /var/log/cron
# Everybody gets emergency messages
*.emerg :omusrmsg:*
# Save news errors of level crit and higher in a special file.
uucp,news.crit /var/log/spooler
# Save boot messages also to boot.log
local7.* /var/log/boot.log
```
为什么我们会访问/var/log/messages,这里很明显,<br />
所有info数据类型都会进入/var/log/messages文件中<br />
这里就要说明Facility(设备)和Severity(日志等级)了<br />
比如我在/etc/rsyslog.conf增加以下代码<br />
```ini
local1.info /var/log/local1.info.log #别忘了增加配置要重启服务哦
# 这里的Facility就是local1,Severity就是info
```
Facility和Severity有以下几种<br />
```
Facility:有0-23种设备
0 kernel messages
1 user-level messages
2 mail system
3 system daemons
4 security/authorization messages
5 messages generated internally by syslogd
6 line printer subsystem
7 network news subsystem
8 UUCP subsystem
9 clock daemon
10 security/authorization messages
11 FTP daemon
12 NTP subsystem
13 log audit
14 log alert
15 clock daemon
16-23 local0 - local7
Severity:日志等级
0 Emergency
1 Alert
2 Critical
3 Error
4 Warning
5 Notice
6 Informational
7 Debug
```
### 问题又来了,我们应该如何写入进local1.info的local1.info.log文件中呢?
这里就涉及到syslog的日志协议了,如图<br />

<br />比如我们要发送到local1.info中<br />
```js
writeLog(514,'127.0.0.1','<142>Jul 26 21:02:30 127.0.0.1 myApp[0]: hello!local1.info!');
```
### 那我们来拆解着一段
#### PRI:
那你<142>怎么来的呢?<br />
因为local1.info对应的Facility是17,Severity是6 <br />
而PRI计算规则是Facility乘8+6即是17乘8+6=142 <br />
#### TIME:
Jul 26 21:02:30 就是时间
#### IP或主机名
127.0.0.1 就是ip,你也可以填写你的主机名如 zsComputer
#### 程序名和错误编号
myApp[0]: 这里myApp就是我们的应用名称(后面可以根据这个做日志拆分),0代表的是该应用的错误编码,如果是错误则大于0
#### 我们要发送的消息
hello!local1.info!
### 接下来我们进去查看信息是否进入
```
tail -f /var/log/local1.info.log
```
这时你应该会看到以下信息在/var/log/local1.info.log文件中
```log
Jul 26 21:02:30 127.0.0.1 hello!local1.info!
```
### 当然规则也有高级用法比如按应用名创建
```ini
$template myFormat,"/var/log/%programname%/%$year%%$month%%$day%.log"
*.* -?myFormat #本机测试使用
#fromhost-ip, !isequal, "127.0.0.1" ?myFormat #实际线上格式
#这里会创建myApp目录并根据年月日生成日志
```
可使用变量如下
```
msg,rawmsg,hostname,source,fromhost,fromhost-ip,syslogtag,programname,pri-text,iut,syslogfacility,syslogfacility-text,syslogseverity,syslogseverity-text,syslogpriority,syslogpriority-text,timegenerated,timereported,timestamp,protocol-version,structured-data,app-name,procid,msgid,inputname
```
系统常量
```
$bom,$now,$year,$month,$day,$hour,$hhour,$qhour,$minute,$myhostname
```
## 简单的rsyslog教程结束
谢谢阅读,除此之外rsyslog还可以结合kafka和hbase,有兴趣可以进行扩展学习哦。
================================================
FILE: article/llvm/.gitignore
================================================
jsvm
jsvm.dSYM
fibo.js.o
main
================================================
FILE: article/llvm/.vscode/settings.json
================================================
{
"files.associations": {
"system_error": "cpp",
"__config": "cpp",
"__nullptr": "cpp",
"cstddef": "cpp",
"exception": "cpp",
"initializer_list": "cpp",
"new": "cpp",
"stdexcept": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"ostream": "cpp",
"__locale": "cpp",
"iostream": "cpp",
"locale": "cpp",
"fstream": "cpp",
"string": "cpp",
"__functional_03": "cpp",
"functional": "cpp",
"iosfwd": "cpp",
"algorithm": "cpp",
"vector": "cpp",
"ios": "cpp",
"__split_buffer": "cpp",
"map": "cpp",
"istream": "cpp",
"tuple": "cpp",
"bitset": "cpp",
"list": "cpp",
"__functional_base": "cpp",
"__functional_base_03": "cpp",
"__tree": "cpp",
"__tuple": "cpp",
"array": "cpp",
"chrono": "cpp",
"limits": "cpp",
"memory": "cpp",
"ratio": "cpp",
"utility": "cpp",
"atomic": "cpp",
"deque": "cpp"
},
"C_Cpp.default.includePath": [
"/usr/local/include",
"/usr/local/opt/llvm/include"
],
}
================================================
FILE: article/llvm/README.md
================================================
# 目录
* [利用LLVM实现JS的编译器,创造属于自己的语言(20180825)](../llvm/jsvm_c/README.md)
* [使用JS实现JS编译器,并将目标js生成二进制(20180908)](../llvm/jsvm_js/README.md)
* [如何自己创建一种编程语言(20190315)](../llvm/how_to_ml/README.md)
================================================
FILE: article/llvm/how_to_ml/README.md
================================================
# 如何自己创建一种编程语言
正好前段时间做了几个JS语言编译器的DEMO,我觉得我可以说说作为一个野路子是如何用LLVM实现语言的。这个还是比较初级的,大佬可以忽略,但或许可以帮助一些刚刚入门想要用LLVM实现语言的朋友。
# 照老虎画猫,实现第一个JS编译器
在制作JS二进制编译器的时候,我首先是参考了LLVM官方的的万花筒语言的编写教程([教程地址](https://github.com/zy445566/llvm-guide-zh/blob/master/README.md))实现了第一个DEMO([DEMO的详细讲解地址](https://github.com/zy445566/myBlog/blob/master/20180825llvm/20180825jsvm_c/README.md)),当然因为是DEMO所以功能不全也BUG多,但是制作套路还是能说说的。制作流程可以分为以下几步。
* 编写AST用于分析语言结构
* 将分析的语言绑定生成IR(中间语言)
* 生成二进制或汇编代码
第一步,简单来说就是写一个代码解析器,把代码解析成一个足够描述代码特征的对象,我们一般叫它AST
第二步,把你这个对象进行一个中间语言IR对象的绑定(LLVM自己实现了一套语言叫IR,这是一个中间语言。)
第三步,就是用LLVM把你绑定好的IR对象,生成IR语言的代码,或者直接生成编译器文件。
可能大家会有点蒙,不是很明白意思。那我一句话总结就是:把你的要实现的语言代码生成一个对象,再把这个对象绑定到IR语言的对象上,LLVM就能直接通过IR语言的对象生成IR代码,IR代码对应了机器码,就可以直接用LLVM生成机器码。其实这一套就是编译器的流程,LLVM最终生成的就是能实现上面功能的编译器了。
走到这里,由于很多人对LLVM还不是很明白,所以上面的话还是不是很懂,那我再白话一点好了。
先提一个问题,如果被关在没有网络的房间里只有一本CPU指令集和一台该CPU的电脑,你会怎么实现一个简单的语言?
其实就很简单了,你只要把你的语言的基础关键词对应到CPU指令集,再基于基础关键字上增加新关键字功能就好了。就比如指针移动粗暴一点直接对应到CPU的跳转地址的指令上就好了,然后再用指针移动实现跳转到地址执行,从而实现类似于IF的功能。
那么基于上面来说LLVM的实现就好讲多了。
那么上面一步就相相当于实现了IR到CPU指令集的转换。
但如果你要实现更好维护的语言,是否要实现IR的一个对象,这样的话只要修改IR对象,再将IR对象生成机器码,是不是就好维护多了?这就是绑定IR的原因。
但你后来发现写IR远远不够我高级语言的需求,同样为了维护性,是不是把我更高级的语言生成AST,再转成IR,这样我是不是就不用转机器码了?这就是我要生成AST树的原因。
# 第二个JS编译器的实现:我能否直接用现成的AST树工具呢?
由于要实现一个语言的AST树所花的时间非常多,个人经历又有限,写出来BUG又多,干嘛不用现成的JS的AST工具呢?
基于这个想法我想到了JS有名的babel来制作我第二个JS编译器([第二个JS编译器的详细讲解地址](https://github.com/zy445566/myBlog/blob/master/20180825llvm/20180908jsvm_js/README.md)),有了AST树转换工具那对于我来说就是如虎添翼了(偷懒了)。
并且直接用node的llvm-node库来做IR绑定,剩下的就用node直接生成编译器了。步骤和上面差不多,我有点累了,就不细讲了,有空自己看吧。
目前来说LLVM出现让语言制造到达了前所未有的方便,否则你可能要考虑创建一个类似汇编的中间语言来实现在多平台运行,同时支持实现转化到不同CPU的机器码,这个很多搞软件编程的同学来说这是相当困难的。还是那句话“LLVM大法好啊”
================================================
FILE: article/llvm/jsvm_c/README.md
================================================
# 利用LLVM实现JS的编译器,创造属于自己的语言
本文参考了官方教程Kaleidoscope语言的实现,本文只实现了JS的编译器的demo,如果想要加深学习比如语言的JIT的实现和语言的代码优化,我将官方教程和代码集合打包在了 [github.com/zy445566/llvm-guide-zh](https://github.com/zy445566/llvm-guide-zh) 中有兴趣,可以更加深入的学习。
# 什么是LLVM
像大家熟知的Swift就是依靠LLVM实现的一门语言,还有Rust也是将LLVM用于后端编译。<br />
一句话总结,它就是一种编译器的基础设施。可能有人说是gcc一类的东西么?老实说最初它却是用来取代gcc的,但它拥有的绝不是编译而是拥有制造新语言能力的全部能力的一个工具。可以让人更加无痛的实现一门语言。<br />
本文编译器流程大概是【编写AST用于分析语言结构】->【将分析的语言绑定生成IR(中间语言)】-> 【生成二进制或汇编代码】 <br />
如果使用LLVM制作语言的虚拟机亦可以实现JIT,或者是编译器和虚拟机的结合体。
# 准备工作
## 安装LLVM
```sh
# centOS,ubuntu应该也可以使用yum或apt-get进行安装
# 有时间的话下载源码编译当然更好
brew install llvm
```
## mac还需要安装xcode命令行工具
```sh
# 两台电脑都装了xcode,一台编译居然找不到标准库
# 这个问题我找了好久
xcode-select --install
```
# 编写AST用于分析语言结构阶段
先定义token类型,用于识别词法结构,定义负数的原因是ascii码的字符都是正数
```h
enum Token{
tok_eof = -1,
// define
tok_var = -2,
tok_func = -3,
// code type
tok_id = -4,
tok_exp = -5,
tok_num = -6,
// choose
tok_if = -7,
tok_else = -8,
// interrupt
tok_return = -9,
// other
tok_unkown = -9999
};
```
解析token的方法,也可以用于字符跳跃
```cpp
static int gettoken()
{
LastChar = fgetc(fp);
// 排除不可见字符
while (isspace(LastChar))
{
LastChar = fgetc(fp);
}
// 排除注释
if (LastChar=='/' && (LastChar = fgetc(fp))=='/'){
do{
LastChar = fgetc(fp);
}
while (!feof(fp) && LastChar != '\n' && LastChar != '\r' && LastChar != 10);
// 吃掉不可见字符
while (isspace(LastChar))
{
LastChar = fgetc(fp);
if (LastChar=='/') {fseek(fp,-1L,SEEK_CUR);}
}
}
// 解析[a-zA-Z][a-zA-Z0-9]*
if (isalpha(LastChar)) {
defineStr = LastChar;
int TmpChar;
while (isalnum((TmpChar = fgetc(fp))) && (LastChar = TmpChar))
{
defineStr += TmpChar;
}
fseek(fp,-1L,SEEK_CUR);
if (defineStr == "var")
{
return tok_var;
}
if (defineStr == "function")
{
return tok_func;
}
if (defineStr == "if")
{
return tok_if;
}
if (defineStr == "else")
{
return tok_else;
}
if (defineStr == "return")
{
return tok_return;
}
return tok_id;
}
// 解析[0-9.]+
if (isdigit(LastChar) || LastChar == '.') {
std::string NumStr;
do {
NumStr += LastChar;
LastChar = fgetc(fp);
} while (isdigit(LastChar) || LastChar == '.');
NumVal = strtod(NumStr.c_str(), nullptr);
return tok_num;
}
if(feof(fp)){
return tok_eof;
}
return LastChar;
}
```
再次定义语法结构数的语法,这个可以根据自己的喜好定义
```h
// AST基类
class ExprAST {
public:
virtual ~ExprAST() = default;
// 这是用于实现IR代码生成的东西
virtual llvm::Value *codegen() = 0;
};
// 定义解析的数字的语法树
class NumberExprAST : public ExprAST {
double Val;
public:
NumberExprAST(double Val) : Val(Val) {}
llvm::Value *codegen() override;
};
// 定义解析的变量的语法树
class VariableExprAST : public ExprAST {
std::string Name;
public:
VariableExprAST(const std::string &Name) : Name(Name) {}
llvm::Value *codegen() override;
};
// 还有很多语法类型,由于太多,暂时不写
...
```
循环获取token并进入对应的方法
```cpp
static void LoopParse() {
while (true) {
LastChar = gettoken();
switch (LastChar) {
case tok_eof:
return;
case ';':
gettoken();
break;
case tok_func:
HandleFunction();
break;
case tok_if:
HandleIf();
break;
default:
break;
}
}
}
```
解析JS方法的功能
```cpp
static std::unique_ptr<FunctionAST> HandleFunction() {
LastChar = gettoken();
// 解析方法的参数
auto Proto = ParsePrototype();
if (!Proto){return nullptr;}
// 吃掉方法的大括号
gettoken();
if (LastChar != '{'){return LogErrorF("Expected '{' in prototype");}
// 定义方法的内容,这是一个数组,因为方法是多行的
std::vector<FnucBody> FnBody;
while(true){
// 这是这一行代码的类型,其中包含表达式和是否返回数据
FnucBody fnRow;
if (auto E = ParseExpression())
{
fnRow.expr_row = std::move(E);
fnRow.tok = RowToken;
RowToken = 0;
FnBody.push_back(std::move(fnRow));
} else {
// 如果这一行是分号,让下一次gettoken去吃掉分号
if (LastChar == ';'){continue;}
// 如果方法结束判断是否有大括号,没有则报异常
if (LastChar != '}'){return LogErrorF("Expected '}' in prototype");}
// 生成方法的AST
auto FnAST = llvm::make_unique<FunctionAST>(std::move(Proto), std::move(FnBody));
// 生成方法的代码
if (auto *FnIR = FnAST->codegen()) {
//异常则输出错误, 未出异常则输出IR
// FnIR->print(llvm::errs());
}
return FnAST;
}
}
return nullptr;
}
```
而里面比较复杂应该是ParseExpression,用于解析表达式的方法,复杂点在于表达式中可能还有表达式,表达式里面还有表达式,有的时候思考下来,脑子里面基本是无限递归,能让脑子瞬间短路
```cpp
// 表达式解析
static std::unique_ptr<ExprAST> ParseExpression() {
// 解析表达式的左边
auto LHS = ParsePrimary();
if (!LHS){
return nullptr;
}
// 解析表达式的操作符和表达式的右边
return ParseBinOpRHS(0, std::move(LHS));
}
// 判断表达式左边是什么类型
static int RowToken = 0;
static std::unique_ptr<ExprAST> ParsePrimary() {
int res = gettoken();
switch (res) {
default:
return LogError("unknown token when expecting an expression");
case tok_id:
// 如果是变量或执行的方法
return ParseIdentifierExpr();
case tok_if:
// 如果是if
return HandleIf();
case tok_num:
// 如果是数字
return ParseNumberExpr();
case tok_return:
// 如果是返回则标记,并继续执行表达式左边
RowToken = tok_return;
return ParsePrimary();
case '}':
// 符号跳过
return nullptr;
case ';':
// 符号跳过
return nullptr;
case '(':
// 作为父表达式运行
return ParseParenExpr();
}
}
// 解析表达式的操作符和表达式的右边
static std::unique_ptr<ExprAST> ParseBinOpRHS(
int ExprPrec,
std::unique_ptr<ExprAST> LHS
) {
gettoken();
while (true) {
// 判断操作符优先级
int TokPrec = GetTokPrecedence();
// 如果操作符优先级低,直接返回当前
if (TokPrec < ExprPrec){return LHS;}
// 如果操作符优先级高,继续运算
int BinOp = LastChar;
// 分析右表达式
auto RHS = ParsePrimary();
if (!RHS){return nullptr;}
// 继续表表达式
int NextPrec = GetTokPrecedence();
// 继续分析操作符优先级
if (TokPrec < NextPrec) {
RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS));
if (!RHS){return nullptr;}
}
// 将左右表达式合并
LHS = llvm::make_unique<BinaryExprAST>(BinOp, std::move(LHS),
std::move(RHS));
}
}
```
# 将分析的语言绑定生成IR(中间语言)
看完上面的是不是觉得有点慌,其实解析好了,生成IR很简单。IR是一个中间语言,简单就是把一门语言转换成另一门语言,而解析好了的话,其实就只剩下绑定了。<br />
先看看方法的AST的定义
```h
// 方法中的一行的类型定义
struct FnucBody{
// 是否有token
int tok;
// 这一行的表达式
std::unique_ptr<ExprAST> expr_row;
};
class FunctionAST {
// 参数列表定义
std::unique_ptr<PrototypeAST> Proto;
// 方法中全部表达式行
std::vector<FnucBody> FnBody;
public:
// 构造
FunctionAST(std::unique_ptr<PrototypeAST> Proto,
std::vector<FnucBody> FnBody)
: Proto(std::move(Proto)), FnBody(std::move(FnBody)) {}
// 定义IRcode的生成方法
llvm::Function *codegen();
};
```
具体生成IR的方法
```h
llvm::Function *FunctionAST::codegen() {
// 获取函数名,并检测是否是已存在的函数
llvm::Function *TheFunction = TheModule->getFunction(Proto->getName());
// 如果函数不存在,则生成行数及参数并将函数重新赋值
if (!TheFunction)
TheFunction = Proto->codegen();
// 如果没生成成功,说明参数存在问题
if (!TheFunction)
return nullptr;
// 在上下文中将entry语法块插入方法中
llvm::BasicBlock *BB = llvm::BasicBlock::Create(TheContext, "entry", TheFunction);
Builder.SetInsertPoint(BB);
// 将参数写入map中
NamedValues.clear();
for (auto &Arg : TheFunction->args())
NamedValues[Arg.getName()] = &Arg;
// 遍历每一行并生成代码,如果token是return,则设置返回数据
for (unsigned i = 0, e = FnBody.size(); i != e; ++i) {
llvm::Value *RetVal = FnBody[i].expr_row->codegen();
if (FnBody[i].tok==tok_return){
Builder.CreateRet(RetVal);
}
// 如果全部的行执行完成则校验方法并返回方法
if(i+1==e){
verifyFunction(*TheFunction);
return TheFunction;
}
}
// 发生错误移除方法
TheFunction->eraseFromParent();
return nullptr;
}
```
# 生成二进制文件
```h
int destFile (std::string FileOrgin) {
// 初始化发出目标代码的所有目标
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();
// 使用我们的目标三元组来获得Target
auto TargetTriple = llvm::sys::getDefaultTargetTriple();
TheModule->setTargetTriple(TargetTriple);
std::string Error;
auto Target = llvm::TargetRegistry::lookupTarget(TargetTriple, Error);
if (!Target) {
llvm::errs() << Error;
return 1;
}
auto CPU = "generic";
auto Features = "";
llvm::TargetOptions opt;
auto RM = llvm::Optional<llvm::Reloc::Model>();
// 将编译的机器信息录入
auto TheTargetMachine =
Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM);
// 通过了解目标和数据布局,优化代码
TheModule->setDataLayout(TheTargetMachine->createDataLayout());
// 定义文件流
std::string Filename = FileOrgin+".o";
std::error_code EC;
llvm::raw_fd_ostream dest(Filename, EC, llvm::sys::fs::F_None);
if (EC) {
llvm::errs() << "Could not open file: " << EC.message();
return 1;
}
// 代码写入流中
llvm::legacy::PassManager pass;
auto FileType = llvm::TargetMachine::CGFT_ObjectFile;
if (TheTargetMachine->addPassesToEmitFile(pass, dest, FileType)) {
llvm::errs() << "TheTargetMachine can't emit a file of this type";
return 1;
}
// 完成并清除流
pass.run(*TheModule);
dest.flush();
// 输出完成提示
llvm::outs() << "Wrote " << Filename << "\n";
return 0;
}
```
# 编译编译器
将我们做好的编译器编译出来,生成jsvm文件
```sh
clang++ -g -O3 jsvm.cpp `llvm-config --cxxflags --ldflags --system-libs --libs all` -o jsvm
```
# 使用我们写好的编译器编译js文件
## 编译js
js文件如下
```js
// fibo.js 这是斐波纳切数
function fibo(num) {
if (num<3) {
return 1;
} else {
return fibo(num-1)+fibo(num-2);
}
}
```
开始编译js文件,将生成 fibo.js.o,如下
```sh
./jsvm fibo.js
```

## 使用c引用js文件,并编译成二进制文件
c代码如下:
```cpp
// main.cpp
#include <iostream>
extern "C" {
double fibo(double);
}
int main() {
std::cout << "fibo(9) is: " << fibo(9) << std::endl;
}
```
编译并运行,如下:
```sh
clang++ main.cpp fibo.js.o -o main && ./main
```

# 总结
第一次写编译器感觉很凌乱,编译器本身来说还算是一个相对复杂的工程,加上js语言的灵活多变性,实现起来可能更加困难,不过这作为一个学习的例子应该是不错的,遂与大家分享。<br />
相信llvm将来也是能为JS助力的,事实上已经有人有很大胆的想法去使用llvm编译JS,前段时间facebook的prepack就有这样一个PR[【facebook/prepack/pull/2264】](https://github.com/facebook/prepack/pull/2264)去实现用llvm将js编译成二进制而无需运行时。兄弟们!JS自举的路或许不会太远了。
================================================
FILE: article/llvm/jsvm_c/fibo.js
================================================
// fibo.js 这是斐波纳切数
function fibo(num) {
if (num<3) {
return 1;
} else {
return fibo(num-1)+fibo(num-2);
}
}
================================================
FILE: article/llvm/jsvm_c/jsvm.cpp
================================================
#include "jsvm.h"
// clang++ -g -O3 jsvm.cpp `llvm-config --cxxflags --ldflags --system-libs --libs all` -o jsvm
// ./jsvm fibo.js && clang++ main.cpp fibo.js.o -o main && ./main
static double NumVal;
static int LastChar;
static std::string defineStr;
static FILE *fp;
static std::map<char, int> BinOp;
static std::unique_ptr<ExprAST> ParseExpression();
static std::unique_ptr<ExprAST> HandleIf();
static int gettoken()
{
LastChar = fgetc(fp);
// 排除不可见字符
while (isspace(LastChar))
{
LastChar = fgetc(fp);
}
// 排除注释
if (LastChar=='/' && (LastChar = fgetc(fp))=='/'){
do{
LastChar = fgetc(fp);
}
while (!feof(fp) && LastChar != '\n' && LastChar != '\r' && LastChar != 10);
// 吃掉不可见字符
while (isspace(LastChar))
{
LastChar = fgetc(fp);
if (LastChar=='/') {fseek(fp,-1L,SEEK_CUR);}
}
}
// 解析[a-zA-Z][a-zA-Z0-9]*
if (isalpha(LastChar)) {
defineStr = LastChar;
int TmpChar;
while (isalnum((TmpChar = fgetc(fp))) && (LastChar = TmpChar))
{
defineStr += TmpChar;
}
fseek(fp,-1L,SEEK_CUR);
if (defineStr == "var")
{
return tok_var;
}
if (defineStr == "function")
{
return tok_func;
}
if (defineStr == "if")
{
return tok_if;
}
if (defineStr == "else")
{
return tok_else;
}
if (defineStr == "return")
{
return tok_return;
}
return tok_id;
}
// 解析[0-9.]+
if (isdigit(LastChar) || LastChar == '.') {
std::string NumStr;
do {
NumStr += LastChar;
LastChar = fgetc(fp);
} while (isdigit(LastChar) || LastChar == '.');
NumVal = strtod(NumStr.c_str(), nullptr);
return tok_num;
}
if(feof(fp)){
return tok_eof;
}
return LastChar;
}
static std::unique_ptr<ExprAST> ParseIdentifierExpr() {
std::string IdName = defineStr;
gettoken();
if (LastChar != '('){
fseek(fp,-1L,SEEK_CUR);
return llvm::make_unique<VariableExprAST>(IdName);
}
std::vector<std::unique_ptr<ExprAST>> Args;
while (true) {
if (auto Arg = ParseExpression()){
Args.push_back(std::move(Arg));
} else {
return nullptr;
}
if (LastChar == ')'){break;}
if (LastChar != ','){return LogError("Expected ')' or ',' in argument list");}
gettoken();
}
return llvm::make_unique<CallExprAST>(IdName, std::move(Args));
}
static std::unique_ptr<ExprAST> ParseNumberExpr() {
auto Result = llvm::make_unique<NumberExprAST>(NumVal);
return std::move(Result);
}
static std::unique_ptr<ExprAST> ParseParenExpr() {
gettoken(); // eat (.
auto V = ParseExpression();
if (!V)
return nullptr;
if (LastChar != ')')
return LogError("expected ')'");
gettoken(); // eat ).
return V;
}
static int RowToken = 0;
static std::unique_ptr<ExprAST> ParsePrimary() {
int res = gettoken();
switch (res) {
default:
return LogError("unknown token when expecting an expression");
case tok_id:
return ParseIdentifierExpr();
case tok_if:
return HandleIf();
case tok_num:
return ParseNumberExpr();
case tok_return:
RowToken = tok_return;
return ParsePrimary();
case '}':
return nullptr;
case ';':
return nullptr;
case '(':
return ParseParenExpr();
}
}
static int GetTokPrecedence() {
if (!isascii(LastChar))
return -1;
int TokPrec = BinOp[LastChar];
if (TokPrec <= 0)
return -1;
return TokPrec;
}
static std::unique_ptr<ExprAST> ParseBinOpRHS(
int ExprPrec,
std::unique_ptr<ExprAST> LHS
) {
gettoken();
while (true) {
int TokPrec = GetTokPrecedence();
if (TokPrec < ExprPrec){return LHS;}
int BinOp = LastChar;
auto RHS = ParsePrimary();
if (!RHS){return nullptr;}
int NextPrec = GetTokPrecedence();
if (TokPrec < NextPrec) {
RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS));
if (!RHS){return nullptr;}
}
// Merge LHS/RHS.
LHS = llvm::make_unique<BinaryExprAST>(BinOp, std::move(LHS),
std::move(RHS));
// return nullptr;
}
}
static std::unique_ptr<ExprAST> ParseExpression() {
auto LHS = ParsePrimary();
if (!LHS){
return nullptr;
}
return ParseBinOpRHS(0, std::move(LHS));
}
static std::unique_ptr<PrototypeAST> ParsePrototype() {
if (LastChar != tok_id){return LogErrorP("Expected function name in prototype");}
std::string FnName = defineStr;
gettoken();
if (LastChar != '('){return LogErrorP("Expected '(' in prototype");}
std::vector<std::string> ArgNames;
while (gettoken() == tok_id || LastChar==',')
{
if(LastChar==','){continue;}
ArgNames.push_back(defineStr);
}
if (LastChar != ')'){return LogErrorP("Expected ')' in prototype");}
return llvm::make_unique<PrototypeAST>(FnName, std::move(ArgNames));
}
static std::unique_ptr<FunctionAST> HandleFunction() {
LastChar = gettoken();
auto Proto = ParsePrototype();
if (!Proto){return nullptr;}
gettoken();
if (LastChar != '{'){return LogErrorF("Expected '{' in prototype");}
std::vector<FnucBody> FnBody;
while(true){
FnucBody fnRow;
if (auto E = ParseExpression())
{
fnRow.expr_row = std::move(E);
fnRow.tok = RowToken;
RowToken = 0;
FnBody.push_back(std::move(fnRow));
} else {
if (LastChar == ';'){continue;}
if (LastChar != '}'){return LogErrorF("Expected '}' in prototype");}
auto FnAST = llvm::make_unique<FunctionAST>(std::move(Proto), std::move(FnBody));
if (auto *FnIR = FnAST->codegen()) {
// FnIR->print(llvm::errs());
}
return FnAST;
}
}
return nullptr;
}
static std::unique_ptr<ExprAST> HandleIf() {
gettoken();
if (LastChar != '('){return LogError("If Expected '(' in prototype");}
// condition.
auto Cond = ParseExpression();
if (!Cond){return nullptr;}
if (LastChar != ')'){return LogError("If Expected ')' in prototype");}
gettoken();
if (LastChar != '{'){return LogError("If Expected '{' in prototype");}
auto Then = ParseExpression();
if (!Then){return nullptr;}
if (LastChar != '}'){return LogError("If Expected '}' in prototype");}
if (gettoken() != tok_else)
return LogError("expected else");
gettoken();
if (LastChar != '{'){return LogError("Else Expected '{' in prototype");}
auto Else = ParseExpression();
if (LastChar ==')') {gettoken();}
if (LastChar ==';') {gettoken();}
if (LastChar != '}'){return LogError("Else Expected '}' in prototype");}
if (!Else){return nullptr;}
fseek(fp,-1L,SEEK_CUR);
return llvm::make_unique<IfExprAST>(std::move(Cond), std::move(Then),
std::move(Else));
}
static void LoopParse() {
while (true) {
LastChar = gettoken();
switch (LastChar) {
case tok_eof:
return;
case ';': // ignore top-level semicolons.
gettoken();
break;
case tok_func:
HandleFunction();
break;
case tok_if:
HandleIf();
break;
default:
break;
}
}
}
int main(int argc, char* argv[])
{
if (argc!=2)
{
printf("args number is error!\r\n");return -1;
}
fp = fopen(argv[1],"r");
if (NULL == fp)
{
printf("file can't open!\r\n");return -1;
}
BinOp['<'] = 10;
BinOp['+'] = 20;
BinOp['-'] = 20;
BinOp['*'] = 40;
TheModule = llvm::make_unique<llvm::Module>("my js jit", TheContext);
LoopParse();
TheModule->print(llvm::errs(), nullptr);
destFile(argv[1]);
fclose(fp);
fp = NULL;
return 0;
}
================================================
FILE: article/llvm/jsvm_c/jsvm.h
================================================
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <memory>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
#include<iostream>
enum Token{
tok_eof = -1,
// define
tok_var = -2,
tok_func = -3,
// code type
tok_id = -4,
tok_exp = -5,
tok_num = -6,
// choose
tok_if = -7,
tok_else = -8,
// interrupt
tok_return = -9,
// other
tok_unkown = -9999
};
static llvm::LLVMContext TheContext;
static llvm::IRBuilder<> Builder(TheContext);
static std::unique_ptr<llvm::Module> TheModule;
static std::map<std::string, llvm::Value *> NamedValues;
namespace {
/// ExprAST - Base class for all expression nodes.
class ExprAST {
public:
virtual ~ExprAST() = default;
virtual llvm::Value *codegen() = 0;
};
/// NumberExprAST - Expression class for numeric literals like "1.0".
class NumberExprAST : public ExprAST {
double Val;
public:
NumberExprAST(double Val) : Val(Val) {}
llvm::Value *codegen() override;
};
/// VariableExprAST - Expression class for referencing a variable, like "a".
class VariableExprAST : public ExprAST {
std::string Name;
public:
VariableExprAST(const std::string &Name) : Name(Name) {}
llvm::Value *codegen() override;
};
/// BinaryExprAST - Expression class for a binary operator.
class BinaryExprAST : public ExprAST {
char Op;
std::unique_ptr<ExprAST> LHS, RHS;
public:
BinaryExprAST(char Op, std::unique_ptr<ExprAST> LHS,
std::unique_ptr<ExprAST> RHS)
: Op(Op), LHS(std::move(LHS)), RHS(std::move(RHS)) {}
llvm::Value *codegen() override;
};
/// CallExprAST - Expression class for function calls.
class CallExprAST : public ExprAST {
std::string Callee;
std::vector<std::unique_ptr<ExprAST>> Args;
public:
CallExprAST(const std::string &Callee,
std::vector<std::unique_ptr<ExprAST>> Args)
: Callee(Callee), Args(std::move(Args)) {}
llvm::Value *codegen() override;
};
// IfExprAST - Expression class for if/then/else.
class IfExprAST : public ExprAST {
std::unique_ptr<ExprAST> Cond, Then, Else;
public:
IfExprAST(std::unique_ptr<ExprAST> Cond, std::unique_ptr<ExprAST> Then,
std::unique_ptr<ExprAST> Else)
: Cond(std::move(Cond)), Then(std::move(Then)), Else(std::move(Else)) {}
llvm::Value *codegen() override;
};
/// PrototypeAST - This class represents the "prototype" for a function,
/// which captures its name, and its argument names (thus implicitly the number
/// of arguments the function takes).
class PrototypeAST {
std::string Name;
std::vector<std::string> Args;
public:
PrototypeAST(const std::string &Name, std::vector<std::string> Args)
: Name(Name), Args(std::move(Args)) {}
llvm::Function *codegen();
const std::string &getName() const { return Name; }
};
/// FunctionAST - This class represents a function definition itself.
struct FnucBody{
int tok;
std::unique_ptr<ExprAST> expr_row;
};
class FunctionAST {
std::unique_ptr<PrototypeAST> Proto;
std::vector<FnucBody> FnBody;
public:
FunctionAST(std::unique_ptr<PrototypeAST> Proto,
std::vector<FnucBody> FnBody)
: Proto(std::move(Proto)), FnBody(std::move(FnBody)) {}
llvm::Function *codegen();
};
}
std::unique_ptr<ExprAST> LogError(const char *Str) {
printf("LogError: %s\n", Str);exit(0);
return nullptr;
}
std::unique_ptr<PrototypeAST> LogErrorP(const char *Str) {
LogError(Str);
return nullptr;
}
std::unique_ptr<FunctionAST> LogErrorF(const char *Str) {
LogError(Str);
return nullptr;
}
llvm::Value *LogErrorV(const char *Str) {
LogError(Str);
return nullptr;
}
llvm::Value *NumberExprAST::codegen() {
return llvm::ConstantFP::get(TheContext, llvm::APFloat(Val));
}
llvm::Value *VariableExprAST::codegen() {
// Look this variable up in the function.
llvm::Value *V = NamedValues[Name];
if (!V)
return LogErrorV("Unknown variable name");
return V;
}
llvm::Value *BinaryExprAST::codegen() {
llvm::Value *L = LHS->codegen();
llvm::Value *R = RHS->codegen();
if (!L || !R)
return nullptr;
switch (Op) {
case '+':
return Builder.CreateFAdd(L, R, "addtmp");
case '-':
return Builder.CreateFSub(L, R, "subtmp");
case '*':
return Builder.CreateFMul(L, R, "multmp");
case '<':
L = Builder.CreateFCmpULT(L, R, "cmptmp");
// Convert bool 0/1 to double 0.0 or 1.0
return Builder.CreateUIToFP(L, llvm::Type::getDoubleTy(TheContext), "booltmp");
default:
return LogErrorV("invalid binary operator");
}
}
llvm::Value *CallExprAST::codegen() {
// Look up the name in the global module table.
llvm::Function *CalleeF = TheModule->getFunction(Callee);
if (!CalleeF)
return LogErrorV("Unknown function referenced");
// If argument mismatch error.
if (CalleeF->arg_size() != Args.size())
return LogErrorV("Incorrect # arguments passed");
std::vector<llvm::Value *> ArgsV;
for (unsigned i = 0, e = Args.size(); i != e; ++i) {
ArgsV.push_back(Args[i]->codegen());
if (!ArgsV.back())
return nullptr;
}
return Builder.CreateCall(CalleeF, ArgsV, "calltmp");
}
llvm::Function *PrototypeAST::codegen() {
// Make the function type: double(double,double) etc.
std::vector<llvm::Type *> Doubles(Args.size(), llvm::Type::getDoubleTy(TheContext));
llvm::FunctionType *FT =
llvm::FunctionType::get(llvm::Type::getDoubleTy(TheContext), Doubles, false);
llvm::Function *F =
llvm::Function::Create(FT, llvm::Function::ExternalLinkage, Name, TheModule.get());
// Set names for all arguments.
unsigned Idx = 0;
for (auto &Arg : F->args())
Arg.setName(Args[Idx++]);
return F;
}
llvm::Function *FunctionAST::codegen() {
// First, check for an existing function from a previous 'extern' declaration.
llvm::Function *TheFunction = TheModule->getFunction(Proto->getName());
if (!TheFunction)
TheFunction = Proto->codegen();
if (!TheFunction)
return nullptr;
// Create a new basic block to start insertion into.
llvm::BasicBlock *BB = llvm::BasicBlock::Create
gitextract_mndo1btf/
├── .gitignore
├── LICENSE
├── README.md
├── SUMMARY.md
└── article/
├── ai/
│ ├── README.md
│ └── tfjs-learn/
│ └── README.md
├── block/
│ ├── README.md
│ ├── bitcoin-key-gen/
│ │ └── README.md
│ ├── ecdsa-secp256k1/
│ │ └── README.md
│ ├── fitblock-white-paper/
│ │ └── README.md
│ ├── ipfs-base/
│ │ └── README.md
│ └── publish-coin/
│ ├── Coin.sol
│ └── README.md
├── design-pattern/
│ ├── README.md
│ └── design-pattern-knowledge/
│ └── README.md
├── docker/
│ ├── README.md
│ └── wsl-remote-docker/
│ └── README.md
├── front/
│ ├── README.md
│ ├── make-full-stack-framework/
│ │ ├── README.md
│ │ └── full-stack-demo/
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── back/
│ │ │ │ ├── router.js
│ │ │ │ ├── src/
│ │ │ │ │ └── controller/
│ │ │ │ │ └── HelloController.js
│ │ │ │ └── static.js
│ │ │ ├── config.js
│ │ │ ├── front/
│ │ │ │ ├── run.js
│ │ │ │ ├── src/
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── static/
│ │ │ │ │ └── index.html
│ │ │ │ └── webpack.config.js
│ │ │ └── middleware/
│ │ │ ├── application-type.js
│ │ │ ├── router.js
│ │ │ └── static.js
│ │ ├── main.js
│ │ └── package.json
│ └── whyneedframework/
│ ├── README.md
│ └── example/
│ ├── README.md
│ ├── app.js
│ ├── components/
│ │ ├── AddList.js
│ │ ├── DataBind.js
│ │ ├── MyBrowseRoute.js
│ │ └── MyRouter.js
│ ├── element-registry.js
│ ├── index.html
│ └── package.json
├── git/
│ ├── README.md
│ ├── git-fake/
│ │ └── README.md
│ └── gitmodules/
│ └── README.md
├── http/
│ ├── README.md
│ ├── http-chunked/
│ │ └── README.md
│ └── node-web-socket/
│ └── README.md
├── leetcode/
│ ├── README.md
│ ├── best-time-to-buy-and-sell-stock/
│ │ ├── README.md
│ │ └── best-time-to-buy-and-sell-stock.js
│ ├── bloom-filter/
│ │ └── README.md
│ ├── build-tree/
│ │ ├── README.md
│ │ └── code/
│ │ ├── list.js
│ │ └── tree.js
│ ├── dynamic-programming/
│ │ └── README.md
│ ├── sudoku-solver/
│ │ ├── README.md
│ │ └── sudokuSolver.js
│ └── three-sum/
│ ├── README.md
│ ├── package.json
│ └── treeSumBenchmark.js
├── linux/
│ ├── README.md
│ ├── centos-danger/
│ │ └── README.md
│ └── rsyslog/
│ └── README.md
├── llvm/
│ ├── .gitignore
│ ├── .vscode/
│ │ └── settings.json
│ ├── README.md
│ ├── how_to_ml/
│ │ └── README.md
│ ├── jsvm_c/
│ │ ├── README.md
│ │ ├── fibo.js
│ │ ├── jsvm.cpp
│ │ ├── jsvm.h
│ │ └── main.cpp
│ └── jsvm_js/
│ ├── .vscode/
│ │ └── settings.json
│ ├── README.md
│ ├── fibo
│ ├── fibo.js
│ ├── fibo.js.bc
│ ├── fibo.s
│ ├── index.js
│ ├── lib/
│ │ ├── jsvm.js
│ │ └── parse.js
│ ├── package.json
│ ├── printDouble.cpp
│ └── printDouble.s
├── node/
│ ├── README.md
│ ├── block-run/
│ │ ├── README.md
│ │ ├── index.js
│ │ ├── package.json
│ │ └── test.sql
│ ├── danger-arguments/
│ │ └── README.md
│ ├── deno/
│ │ ├── README.md
│ │ ├── build/
│ │ │ └── README.md
│ │ ├── exends/
│ │ │ └── README.md
│ │ └── frame/
│ │ ├── README.md
│ │ └── jsFrame/
│ │ ├── README.md
│ │ ├── config.js
│ │ ├── controller/
│ │ │ └── HelloController.js
│ │ ├── engineMiddle/
│ │ │ ├── deno/
│ │ │ │ ├── file.js
│ │ │ │ ├── http.js
│ │ │ │ └── mod.js
│ │ │ └── node/
│ │ │ ├── custom-loader.mjs
│ │ │ ├── file.js
│ │ │ ├── http.js
│ │ │ └── index.js
│ │ ├── main.js
│ │ ├── router.js
│ │ ├── run.sh
│ │ └── server/
│ │ ├── HelloServer.js
│ │ └── db.json
│ ├── eventloop/
│ │ └── README.md
│ ├── left-shift-operator-keng/
│ │ └── README.md
│ ├── memory-leak/
│ │ ├── README.md
│ │ ├── actual/
│ │ │ └── README.md
│ │ ├── require-vm/
│ │ │ ├── README.md
│ │ │ ├── case1-require-vm.js
│ │ │ ├── case1-require.js
│ │ │ ├── case1.js
│ │ │ ├── case2-require-vm.js
│ │ │ ├── case2-require.js
│ │ │ ├── case3.js
│ │ │ └── package.json
│ │ └── vm-save-leak/
│ │ └── README.md
│ ├── mmap/
│ │ ├── README.md
│ │ └── share-object/
│ │ ├── README.md
│ │ ├── binding.gyp
│ │ ├── build/
│ │ │ ├── Makefile
│ │ │ ├── Release/
│ │ │ │ ├── .deps/
│ │ │ │ │ └── Release/
│ │ │ │ │ ├── obj.target/
│ │ │ │ │ │ └── shareObject/
│ │ │ │ │ │ └── shareObject.o.d
│ │ │ │ │ └── shareObject.node.d
│ │ │ │ ├── obj.target/
│ │ │ │ │ └── shareObject/
│ │ │ │ │ └── shareObject.o
│ │ │ │ └── shareObject.node
│ │ │ ├── binding.Makefile
│ │ │ ├── config.gypi
│ │ │ ├── gyp-mac-tool
│ │ │ └── shareObject.target.mk
│ │ ├── package.json
│ │ ├── share-data
│ │ ├── shareObject.cc
│ │ ├── shareRead.js
│ │ └── shareWrite.js
│ ├── multi-threaded/
│ │ └── README.md
│ ├── ncpu-resue-threaded/
│ │ └── README.md
│ ├── node-debugger/
│ │ └── README.md
│ ├── node-rust-bindings/
│ │ ├── .gitignore
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── fib/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── lib/
│ │ │ └── index.js
│ │ ├── native/
│ │ │ ├── Cargo.toml
│ │ │ ├── build.rs
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── package.json
│ ├── npmtaobaobye/
│ │ └── README.md
│ ├── opencv-getface.js/
│ │ ├── README.md
│ │ └── example/
│ │ ├── haarcascade_frontalface_default.xml
│ │ ├── index.html
│ │ ├── node-index.js
│ │ ├── opencv.js
│ │ ├── package.json
│ │ └── utils.js
│ ├── quickjs/
│ │ ├── README.md
│ │ └── touch/
│ │ └── README.md
│ ├── stream-question/
│ │ └── README.md
│ ├── type-pointer/
│ │ └── README.md
│ ├── why-await-not-block/
│ │ └── README.md
│ └── whyheighqps/
│ ├── README.md
│ ├── TestServlet.java
│ └── test.js
├── serverless/
│ ├── README.md
│ ├── first-use/
│ │ ├── README.md
│ │ └── test.js
│ └── knative-faas/
│ ├── README.md
│ ├── hello-world/
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── index.js
│ │ ├── package.json
│ │ └── service.yaml
│ └── op.md
├── shadowsocks/
│ ├── README.md
│ └── shadowsocks-rust/
│ └── README.md
├── v8/
│ ├── README.md
│ ├── add_http/
│ │ ├── README.md
│ │ ├── http.cc
│ │ ├── http.h
│ │ ├── http.js
│ │ ├── lastBUILD.gn
│ │ └── zy_node.cc
│ ├── ninja_v8/
│ │ └── README.md
│ └── run_js/
│ ├── README.md
│ ├── lastBUILD.gn
│ └── zy_node.cc
├── wasm/
│ ├── README.md
│ └── emscripten-calling-c/
│ ├── README.md
│ ├── add.c
│ ├── add.js
│ ├── add.wasm
│ └── test.js
└── windows/
├── README.md
└── choco/
└── README.md
SYMBOL INDEX (467 symbols across 47 files)
FILE: article/front/make-full-stack-framework/full-stack-demo/app/back/src/controller/HelloController.js
class HelloController (line 1) | class HelloController {
method world (line 2) | async world(ctx) {
FILE: article/front/make-full-stack-framework/full-stack-demo/app/front/run.js
function runBack (line 11) | function runBack(err, stats) {
FILE: article/front/make-full-stack-framework/full-stack-demo/app/front/src/index.jsx
class App (line 4) | class App extends React.Component {
method constructor (line 5) | constructor(props) {
method render (line 17) | render() {
FILE: article/front/make-full-stack-framework/full-stack-demo/app/middleware/static.js
function readFile (line 4) | function readFile(filePath) {
function switchTypeByExt (line 12) | function switchTypeByExt(extname) {
FILE: article/front/whyneedframework/example/app.js
class AppContainer (line 3) | class AppContainer extends HTMLElement {
method constructor (line 4) | constructor() {
FILE: article/front/whyneedframework/example/components/AddList.js
class AddList (line 1) | class AddList extends HTMLElement {
method constructor (line 2) | constructor() {
method getHtml (line 23) | getHtml() {
method getLi (line 30) | getLi() {
FILE: article/front/whyneedframework/example/components/DataBind.js
class DataBind (line 1) | class DataBind extends HTMLElement {
method constructor (line 2) | constructor() {
method dataBind (line 14) | dataBind(data) {
method addEvent (line 23) | addEvent() {
method changeVal (line 29) | changeVal(e) {
method getHtml (line 35) | getHtml() {
FILE: article/front/whyneedframework/example/components/MyBrowseRoute.js
class MyBrowseRoute (line 1) | class MyBrowseRoute extends HTMLElement {
method constructor (line 2) | constructor() {
method getHtml (line 12) | getHtml() {
FILE: article/front/whyneedframework/example/components/MyRouter.js
class MyRouter (line 1) | class MyRouter extends HTMLElement {
method constructor (line 2) | constructor() {
FILE: article/leetcode/build-tree/code/list.js
function ListNode (line 1) | function ListNode(val, next) {
function buileListByArray (line 5) | function buileListByArray(list) {
FILE: article/leetcode/build-tree/code/tree.js
function TreeNode (line 1) | function TreeNode(val, left, right) {
function buileTreeByArray (line 6) | function buileTreeByArray(list) {
FILE: article/leetcode/sudoku-solver/sudokuSolver.js
function delUnkownNum (line 7) | function delUnkownNum (setMap,x,y) {
function checkNowNum (line 12) | function checkNowNum(x,y) {
function eachNum (line 35) | function eachNum() {
FILE: article/leetcode/three-sum/treeSumBenchmark.js
function beatRepeat (line 30) | function beatRepeat(num1,num2,num3) {
function beatRepeat (line 56) | function beatRepeat(num1,num2,num3) {
function beatRepeat (line 99) | function beatRepeat(num1,num2,num3) {
FILE: article/llvm/jsvm_c/fibo.js
function fibo (line 2) | function fibo(num) {
FILE: article/llvm/jsvm_c/jsvm.cpp
function gettoken (line 14) | static int gettoken()
function ParseIdentifierExpr (line 82) | static std::unique_ptr<ExprAST> ParseIdentifierExpr() {
function ParseNumberExpr (line 104) | static std::unique_ptr<ExprAST> ParseNumberExpr() {
function ParseParenExpr (line 109) | static std::unique_ptr<ExprAST> ParseParenExpr() {
function ParsePrimary (line 122) | static std::unique_ptr<ExprAST> ParsePrimary() {
function GetTokPrecedence (line 145) | static int GetTokPrecedence() {
function ParseBinOpRHS (line 154) | static std::unique_ptr<ExprAST> ParseBinOpRHS(
function ParseExpression (line 177) | static std::unique_ptr<ExprAST> ParseExpression() {
function ParsePrototype (line 185) | static std::unique_ptr<PrototypeAST> ParsePrototype() {
function HandleFunction (line 201) | static std::unique_ptr<FunctionAST> HandleFunction() {
function HandleIf (line 229) | static std::unique_ptr<ExprAST> HandleIf() {
function LoopParse (line 255) | static void LoopParse() {
function main (line 276) | int main(int argc, char* argv[])
FILE: article/llvm/jsvm_c/jsvm.h
type Token (line 36) | enum Token{
function class (line 62) | class ExprAST {
function class (line 69) | class NumberExprAST : public ExprAST {
function class (line 78) | class VariableExprAST : public ExprAST {
function class (line 87) | class BinaryExprAST : public ExprAST {
function class (line 99) | class CallExprAST : public ExprAST {
function class (line 111) | class IfExprAST : public ExprAST {
function class (line 124) | class PrototypeAST {
type FnucBody (line 136) | struct FnucBody{
function class (line 140) | class FunctionAST {
function destFile (line 332) | int destFile (std::string FileOrgin) {
FILE: article/llvm/jsvm_c/main.cpp
function main (line 8) | int main() {
FILE: article/llvm/jsvm_js/fibo.js
function fibo (line 2) | function fibo(num) {
function main (line 7) | function main()
FILE: article/llvm/jsvm_js/lib/jsvm.js
method constructor (line 13) | constructor(js_ast) {
method getPrintDouble (line 17) | getPrintDouble()
method init (line 34) | init()
method programHandler (line 39) | programHandler(node) {
method functionHandler (line 46) | functionHandler(node) {
method blockHandler (line 89) | blockHandler(node)
method ifHandler (line 99) | ifHandler(node) {
method binaryHandler (line 137) | binaryHandler(node) {
method returnHandler (line 172) | returnHandler(node) {
method identifierHandler (line 177) | identifierHandler(node,parent_node) {
method numberHandler (line 190) | numberHandler(node) {
method stringHandler (line 194) | stringHandler(node) {
method callHandler (line 198) | callHandler(node) {
method expressionHandler (line 223) | expressionHandler(node) {
method handler (line 227) | handler(node,parent_node = null) {
method gen (line 255) | gen() {
method print (line 259) | print() {
method write (line 262) | write(bit_code_path) {
FILE: article/llvm/jsvm_js/printDouble.cpp
function printDouble (line 7) | double printDouble(double double_num) {
FILE: article/node/block-run/index.js
function sqlClear (line 31) | async function sqlClear()
function getName (line 40) | function getName(count)
function sqlCommon (line 45) | async function sqlCommon(sqlCommonName = 'sqlCommon')
function beginTransaction (line 79) | function beginTransaction(conn)
function rollback (line 89) | function rollback(conn)
function commit (line 99) | function commit(conn)
function sqlTrans (line 109) | async function sqlTrans()
function sqlBlock (line 149) | function sqlBlock()
FILE: article/node/block-run/test.sql
type `test` (line 2) | CREATE TABLE IF NOT EXISTS `test`.`test` (
FILE: article/node/deno/frame/jsFrame/controller/HelloController.js
class HelloController (line 2) | class HelloController {
method constructor (line 3) | constructor() {
method hello (line 7) | async hello() {
FILE: article/node/deno/frame/jsFrame/engineMiddle/deno/file.js
class FileManger (line 2) | class FileManger {
method getContent (line 3) | static async getContent(url,filePath) {
FILE: article/node/deno/frame/jsFrame/engineMiddle/deno/http.js
class Http (line 2) | class Http {
method constructor (line 3) | constructor(router) {
method listen (line 7) | async listen (port) {
FILE: article/node/deno/frame/jsFrame/engineMiddle/node/custom-loader.mjs
constant JS_EXTENSIONS (line 6) | const JS_EXTENSIONS = new Set(['.js', '.mjs']);
function resolve (line 11) | function resolve(specifier, parentModuleURL = baseURL, defaultResolve) {
FILE: article/node/deno/frame/jsFrame/engineMiddle/node/file.js
class FileManger (line 3) | class FileManger {
method getContent (line 4) | static async getContent(url,filePath) {
FILE: article/node/deno/frame/jsFrame/engineMiddle/node/http.js
class Http (line 2) | class Http {
method constructor (line 3) | constructor(router) {
method listen (line 7) | async listen (port) {
FILE: article/node/deno/frame/jsFrame/engineMiddle/node/index.js
function getMidInjectObj (line 6) | function getMidInjectObj() {
method initializeImportMeta (line 24) | initializeImportMeta(meta){
function linker (line 27) | async function linker(specifier, referencingModule) {
FILE: article/node/deno/frame/jsFrame/router.js
class Router (line 2) | class Router {
method constructor (line 3) | constructor() {
method getRouterMap (line 6) | getRouterMap() {
FILE: article/node/deno/frame/jsFrame/server/HelloServer.js
class HelloServer (line 1) | class HelloServer {
method constructor (line 2) | constructor () {
method hello (line 5) | async hello() {
FILE: article/node/memory-leak/require-vm/case2-require-vm.js
function getN (line 2) | function getN (n) {
FILE: article/node/memory-leak/require-vm/case2-require.js
function getN (line 1) | function getN (n) {
FILE: article/node/mmap/share-object/shareObject.cc
function napi_value (line 11) | napi_value createInt(napi_env env,long num) {
function get_share_adress (line 19) | long get_share_adress(size_t min_size,int is_init=0) {
function napi_value (line 31) | napi_value readMethod(napi_env env, napi_callback_info info) {
function napi_value (line 41) | napi_value writeMethod(napi_env env, napi_callback_info info) {
function napi_value (line 58) | napi_value Init(napi_env env, napi_value exports) {
FILE: article/node/node-rust-bindings/fib/native/build.rs
function main (line 3) | fn main() {
FILE: article/node/node-rust-bindings/fib/native/src/lib.rs
function hello (line 10) | fn hello(call: Call) -> JsResult<JsString> {
function fib (line 16) | fn fib(call: Call) -> JsResult<JsInteger> {
function easy_fib (line 33) | fn easy_fib(num:i32) -> i32
FILE: article/node/opencv-getface.js/example/node-index.js
method onRuntimeInitialized (line 2) | async onRuntimeInitialized() {
function getFace (line 12) | async function getFace() {
FILE: article/node/opencv-getface.js/example/opencv.js
function locateFile (line 30) | function locateFile(path){if(Module["locateFile"]){return Module["locate...
function dynamicAlloc (line 30) | function dynamicAlloc(size){var ret=HEAP32[DYNAMICTOP_PTR>>2];var end=re...
function getNativeTypeSize (line 30) | function getNativeTypeSize(type){switch(type){case"i1":case"i8":return 1...
function warnOnce (line 30) | function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnc...
function convertJsFunctionToWasm (line 30) | function convertJsFunctionToWasm(func,sig){var typeSection=[1,0,1,96];va...
function addFunctionWasm (line 30) | function addFunctionWasm(func,sig){var table=wasmTable;var ret=table.len...
function removeFunctionWasm (line 30) | function removeFunctionWasm(index){}
function dynCall (line 30) | function dynCall(sig,ptr,args){if(args&&args.length){return Module["dynC...
function setValue (line 30) | function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(...
function assert (line 30) | function assert(condition,text){if(!condition){abort("Assertion failed: ...
function getCFunc (line 30) | function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot ...
function ccall (line 30) | function ccall(ident,returnType,argTypes,args,opts){var toC={"string":fu...
function allocate (line 30) | function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof ...
function getMemory (line 30) | function getMemory(size){if(!runtimeInitialized)return dynamicAlloc(size...
function UTF8ArrayToString (line 30) | function UTF8ArrayToString(u8Array,idx,maxBytesToRead){var endIdx=idx+ma...
function UTF8ToString (line 30) | function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(H...
function stringToUTF8Array (line 30) | function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(m...
function stringToUTF8 (line 30) | function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Arr...
function lengthBytesUTF8 (line 30) | function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){va...
function writeArrayToMemory (line 30) | function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}
function writeAsciiToMemory (line 30) | function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.le...
function alignUp (line 30) | function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}ret...
function updateGlobalBufferAndViews (line 30) | function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP...
function callRuntimeCallbacks (line 30) | function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var c...
function preRun (line 30) | function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="func...
function initRuntime (line 30) | function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!...
function preMain (line 30) | function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATM...
function exitRuntime (line 30) | function exitRuntime(){runtimeExited=true}
function postRun (line 30) | function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="f...
function addOnPreRun (line 30) | function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}
function addOnPostRun (line 30) | function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}
function getUniqueRunDependency (line 30) | function getUniqueRunDependency(id){return id}
function addRunDependency (line 30) | function addRunDependency(id){runDependencies++;if(Module["monitorRunDep...
function removeRunDependency (line 30) | function removeRunDependency(id){runDependencies--;if(Module["monitorRun...
function abort (line 30) | function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+...
function isDataURI (line 30) | function isDataURI(filename){return String.prototype.startsWith?filename...
function getBinary (line 30) | function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary...
function getBinaryPromise (line 30) | function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRON...
function createWasm (line 30) | function createWasm(){var info={"env":asmLibraryArg,"wasi_unstable":asmL...
function _emscripten_set_main_loop_timing (line 30) | function _emscripten_set_main_loop_timing(mode,value){Browser.mainLoop.t...
function _emscripten_get_now (line 30) | function _emscripten_get_now(){abort()}
function _emscripten_set_main_loop (line 30) | function _emscripten_set_main_loop(func,fps,simulateInfiniteLoop,arg,noS...
function finish (line 30) | function finish(audio){if(done)return;done=true;Module["preloadedAudios"...
function fail (line 30) | function fail(){if(done)return;done=true;Module["preloadedAudios"][name]...
function encode64 (line 30) | function encode64(data){var BASE="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkl...
function pointerLockChange (line 30) | function pointerLockChange(){Browser.pointerLock=document["pointerLockEl...
function fullscreenChange (line 30) | function fullscreenChange(){Browser.isFullscreen=false;var canvasContain...
function demangle (line 30) | function demangle(func){var __cxa_demangle_func=Module["___cxa_demangle"...
function demangleAll (line 30) | function demangleAll(text){var regex=/\b_Z[\w\d_]+/g;return text.replace...
function jsStackTrace (line 30) | function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new E...
function stackTrace (line 30) | function stackTrace(){var js=jsStackTrace();if(Module["extraStackTrace"]...
function ___cxa_allocate_exception (line 30) | function ___cxa_allocate_exception(size){return _malloc(size)}
function _atexit (line 30) | function _atexit(func,arg){__ATEXIT__.unshift({func:func,arg:arg})}
function ___cxa_atexit (line 30) | function ___cxa_atexit(){return _atexit.apply(null,arguments)}
function ___cxa_throw (line 30) | function ___cxa_throw(ptr,type,destructor){___exception_infos[ptr]={ptr:...
function ___lock (line 30) | function ___lock(){}
function ___setErrNo (line 30) | function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module...
function ___map_file (line 30) | function ___map_file(pathname,size){___setErrNo(63);return-1}
function trim (line 30) | function trim(arr){var start=0;for(;start<arr.length;start++){if(arr[sta...
function isRealDir (line 30) | function isRealDir(p){return p!=="."&&p!==".."}
function toAbsolute (line 30) | function toAbsolute(root){return function(p){return PATH.join2(root,p)}}
function done (line 30) | function done(err){if(err&&!errored){errored=true;return callback(err)}}
function ensureParent (line 30) | function ensureParent(path){var parts=path.split("/");var parent=root;fo...
function base (line 30) | function base(path){var parts=path.split("/");return parts[parts.length-1]}
function doCallback (line 30) | function doCallback(err){FS.syncFSRequests--;return callback(err)}
function done (line 30) | function done(err){if(err){if(!done.errored){done.errored=true;return do...
function LazyUint8Array (line 30) | function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}
function processData (line 30) | function processData(byteArray){function finish(byteArray){if(preFinish)...
function finish (line 30) | function finish(){if(fail==0)onload();else onerror()}
function finish (line 30) | function finish(){if(fail==0)onload();else onerror()}
function ___syscall221 (line 30) | function ___syscall221(which,varargs){SYSCALLS.varargs=varargs;try{var s...
function ___syscall3 (line 30) | function ___syscall3(which,varargs){SYSCALLS.varargs=varargs;try{var str...
function ___syscall4 (line 30) | function ___syscall4(which,varargs){SYSCALLS.varargs=varargs;try{var str...
function ___syscall5 (line 30) | function ___syscall5(which,varargs){SYSCALLS.varargs=varargs;try{var pat...
function ___syscall54 (line 30) | function ___syscall54(which,varargs){SYSCALLS.varargs=varargs;try{var st...
function __emscripten_syscall_munmap (line 30) | function __emscripten_syscall_munmap(addr,len){if(addr===-1||len===0){re...
function ___syscall91 (line 30) | function ___syscall91(which,varargs){SYSCALLS.varargs=varargs;try{var ad...
function ___unlock (line 30) | function ___unlock(){}
function runDestructors (line 30) | function runDestructors(destructors){while(destructors.length){var ptr=d...
function simpleReadValueFromPointer (line 30) | function simpleReadValueFromPointer(pointer){return this["fromWireType"]...
function makeLegalFunctionName (line 30) | function makeLegalFunctionName(name){if(undefined===name){return"_unknow...
function createNamedFunction (line 30) | function createNamedFunction(name,body){name=makeLegalFunctionName(name)...
function extendError (line 30) | function extendError(baseErrorType,errorName){var errorClass=createNamed...
function throwInternalError (line 30) | function throwInternalError(message){throw new InternalError(message)}
function whenDependentTypesAreResolved (line 30) | function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeCon...
function __embind_finalize_value_array (line 30) | function __embind_finalize_value_array(rawTupleType){var reg=tupleRegist...
function __embind_finalize_value_object (line 30) | function __embind_finalize_value_object(structType){var reg=structRegist...
function getShiftFromSize (line 30) | function getShiftFromSize(size){switch(size){case 1:return 0;case 2:retu...
function embind_init_charCodes (line 30) | function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<...
function readLatin1String (line 30) | function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret...
function throwBindingError (line 30) | function throwBindingError(message){throw new BindingError(message)}
function registerType (line 30) | function registerType(rawType,registeredInstance,options){options=option...
function __embind_register_bool (line 30) | function __embind_register_bool(rawType,name,size,trueValue,falseValue){...
function ClassHandle_isAliasOf (line 30) | function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle))...
function shallowCopyInternalPointer (line 30) | function shallowCopyInternalPointer(o){return{count:o.count,deleteSchedu...
function throwInstanceAlreadyDeleted (line 30) | function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(h...
function detachFinalizer (line 30) | function detachFinalizer(handle){}
function runDestructor (line 30) | function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor...
function releaseClassHandle (line 30) | function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.co...
function attachFinalizer (line 30) | function attachFinalizer(handle){if("undefined"===typeof FinalizationGro...
function ClassHandle_clone (line 30) | function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDelete...
function ClassHandle_delete (line 30) | function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDelet...
function ClassHandle_isDeleted (line 30) | function ClassHandle_isDeleted(){return!this.$$.ptr}
function flushPendingDeletes (line 30) | function flushPendingDeletes(){while(deletionQueue.length){var obj=delet...
function ClassHandle_deleteLater (line 30) | function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlready...
function init_ClassHandle (line 30) | function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHand...
function ClassHandle (line 30) | function ClassHandle(){}
function ensureOverloadTable (line 30) | function ensureOverloadTable(proto,methodName,humanName){if(undefined===...
function exposePublicSymbol (line 30) | function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnPro...
function RegisteredClass (line 30) | function RegisteredClass(name,constructor,instancePrototype,rawDestructo...
function upcastPointer (line 30) | function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desir...
function constNoSmartPtrRawPointerToWireType (line 30) | function constNoSmartPtrRawPointerToWireType(destructors,handle){if(hand...
function genericPointerToWireType (line 30) | function genericPointerToWireType(destructors,handle){var ptr;if(handle=...
function nonConstNoSmartPtrRawPointerToWireType (line 30) | function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(h...
function RegisteredPointer_getPointee (line 30) | function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=th...
function RegisteredPointer_destructor (line 30) | function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.r...
function RegisteredPointer_deleteObject (line 30) | function RegisteredPointer_deleteObject(handle){if(handle!==null){handle...
function downcastPointer (line 30) | function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desire...
function getInheritedInstanceCount (line 30) | function getInheritedInstanceCount(){return Object.keys(registeredInstan...
function getLiveInheritedInstances (line 30) | function getLiveInheritedInstances(){var rv=[];for(var k in registeredIn...
function setDelayFunction (line 30) | function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&...
function init_embind (line 30) | function init_embind(){Module["getInheritedInstanceCount"]=getInheritedI...
function getBasestPointer (line 30) | function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingEr...
function getInheritedInstance (line 30) | function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,pt...
function makeClassHandle (line 30) | function makeClassHandle(prototype,record){if(!record.ptrType||!record.p...
function RegisteredPointer_fromWireType (line 30) | function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPoin...
function init_RegisteredPointer (line 30) | function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee...
function RegisteredPointer (line 30) | function RegisteredPointer(name,registeredClass,isReference,isConst,isSm...
function replacePublicSymbol (line 30) | function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnP...
function embind__requireFunction (line 30) | function embind__requireFunction(signature,rawFunction){signature=readLa...
function getTypeName (line 30) | function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin...
function throwUnboundTypeError (line 30) | function throwUnboundTypeError(message,types){var unboundTypes=[];var se...
function __embind_register_class (line 30) | function __embind_register_class(rawType,rawPointerType,rawConstPointerT...
function new_ (line 30) | function new_(constructor,argumentList){if(!(constructor instanceof Func...
function craftInvokerFunction (line 30) | function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFun...
function heap32VectorToArray (line 30) | function heap32VectorToArray(count,firstElement){var array=[];for(var i=...
function __embind_register_class_class_function (line 30) | function __embind_register_class_class_function(rawClassType,methodName,...
function __embind_register_class_constructor (line 30) | function __embind_register_class_constructor(rawClassType,argCount,rawAr...
function __embind_register_class_function (line 30) | function __embind_register_class_function(rawClassType,methodName,argCou...
function validateThis (line 30) | function validateThis(this_,classType,humanName){if(!(this_ instanceof O...
function __embind_register_class_property (line 30) | function __embind_register_class_property(classType,fieldName,getterRetu...
function __embind_register_constant (line 30) | function __embind_register_constant(name,type,value){name=readLatin1Stri...
function __emval_decref (line 30) | function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[ha...
function count_emval_handles (line 30) | function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_ar...
function get_first_emval (line 30) | function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){...
function init_emval (line 30) | function init_emval(){Module["count_emval_handles"]=count_emval_handles;...
function __emval_register (line 30) | function __emval_register(value){switch(value){case undefined:{return 1}...
function __embind_register_emval (line 30) | function __embind_register_emval(rawType,name){name=readLatin1String(nam...
function _embind_repr (line 30) | function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==...
function floatReadValueFromPointer (line 30) | function floatReadValueFromPointer(name,shift){switch(shift){case 2:retu...
function __embind_register_float (line 30) | function __embind_register_float(rawType,name,size){var shift=getShiftFr...
function __embind_register_function (line 30) | function __embind_register_function(name,argCount,rawArgTypesAddr,signat...
function integerReadValueFromPointer (line 30) | function integerReadValueFromPointer(name,shift,signed){switch(shift){ca...
function __embind_register_integer (line 30) | function __embind_register_integer(primitiveType,name,size,minRange,maxR...
function __embind_register_memory_view (line 30) | function __embind_register_memory_view(rawType,dataTypeIndex,name){var t...
function __embind_register_smart_ptr (line 30) | function __embind_register_smart_ptr(rawType,rawPointeeType,name,sharing...
function __embind_register_std_string (line 30) | function __embind_register_std_string(rawType,name){name=readLatin1Strin...
function __embind_register_std_wstring (line 30) | function __embind_register_std_wstring(rawType,charSize,name){name=readL...
function __embind_register_value_array (line 30) | function __embind_register_value_array(rawType,name,constructorSignature...
function __embind_register_value_array_element (line 30) | function __embind_register_value_array_element(rawTupleType,getterReturn...
function __embind_register_value_object (line 30) | function __embind_register_value_object(rawType,name,constructorSignatur...
function __embind_register_value_object_field (line 30) | function __embind_register_value_object_field(structType,fieldName,gette...
function __embind_register_void (line 30) | function __embind_register_void(rawType,name){name=readLatin1String(name...
function requireHandle (line 30) | function requireHandle(handle){if(!handle){throwBindingError("Cannot use...
function requireRegisteredType (line 30) | function requireRegisteredType(rawType,humanName){var impl=registeredTyp...
function __emval_as (line 30) | function __emval_as(handle,returnType,destructorsRef){handle=requireHand...
function getStringOrSymbol (line 30) | function getStringOrSymbol(address){var symbol=emval_symbols[address];if...
function __emval_call_void_method (line 30) | function __emval_call_void_method(caller,handle,methodName,args){caller=...
function __emval_addMethodCaller (line 30) | function __emval_addMethodCaller(caller){var id=emval_methodCallers.leng...
function __emval_lookupTypes (line 30) | function __emval_lookupTypes(argCount,argTypes,argWireTypes){var a=new A...
function __emval_get_method_caller (line 30) | function __emval_get_method_caller(argCount,argTypes){var types=__emval_...
function __emval_get_property (line 30) | function __emval_get_property(handle,key){handle=requireHandle(handle);k...
function __emval_incref (line 30) | function __emval_incref(handle){if(handle>4){emval_handle_array[handle]....
function __emval_new_array (line 30) | function __emval_new_array(){return __emval_register([])}
function __emval_new_cstring (line 30) | function __emval_new_cstring(v){return __emval_register(getStringOrSymbo...
function __emval_run_destructors (line 30) | function __emval_run_destructors(handle){var destructors=emval_handle_ar...
function __emval_set_property (line 30) | function __emval_set_property(handle,key,value){handle=requireHandle(han...
function __emval_take_value (line 30) | function __emval_take_value(type,argv){type=requireRegisteredType(type,"...
function _abort (line 30) | function _abort(){abort()}
function _emscripten_get_heap_size (line 30) | function _emscripten_get_heap_size(){return HEAP8.length}
function _emscripten_get_sbrk_ptr (line 30) | function _emscripten_get_sbrk_ptr(){return 1273136}
function _emscripten_memcpy_big (line 30) | function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray...
function emscripten_realloc_buffer (line 30) | function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer...
function _emscripten_resize_heap (line 30) | function _emscripten_resize_heap(requestedSize){var oldSize=_emscripten_...
function _emscripten_get_environ (line 30) | function _emscripten_get_environ(){if(!_emscripten_get_environ.strings){...
function _environ_get (line 30) | function _environ_get(__environ,environ_buf){var strings=_emscripten_get...
function _environ_sizes_get (line 30) | function _environ_sizes_get(penviron_count,penviron_buf_size){var string...
function _fd_close (line 30) | function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.cl...
function _fd_read (line 30) | function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamF...
function _fd_seek (line 30) | function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var st...
function _fd_write (line 30) | function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStream...
function _gettimeofday (line 30) | function _gettimeofday(ptr){var now=Date.now();HEAP32[ptr>>2]=now/1e3|0;...
function _pthread_mutexattr_destroy (line 30) | function _pthread_mutexattr_destroy(){}
function _pthread_mutexattr_init (line 30) | function _pthread_mutexattr_init(){}
function _pthread_mutexattr_settype (line 30) | function _pthread_mutexattr_settype(){}
function _sched_yield (line 30) | function _sched_yield(){return 0}
function _setTempRet0 (line 30) | function _setTempRet0($i){setTempRet0($i|0)}
function __isLeapYear (line 30) | function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400==...
function __arraySum (line 30) | function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=arr...
function __addDays (line 30) | function __addDays(date,days){var newDate=new Date(date.getTime());while...
function _strftime (line 30) | function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var...
function _strftime_l (line 30) | function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,for...
function _sysconf (line 30) | function _sysconf(name){switch(name){case 30:return PAGE_SIZE;case 85:va...
function intArrayFromString (line 30) | function intArrayFromString(stringy,dontAddNull,length){var len=length>0...
function intArrayToString (line 30) | function intArrayToString(array){var ret=[];for(var i=0;i<array.length;i...
function intArrayFromBase64 (line 30) | function intArrayFromBase64(s){if(typeof ENVIRONMENT_IS_NODE==="boolean"...
function tryParseAsDataURI (line 30) | function tryParseAsDataURI(filename){if(!isDataURI(filename)){return}ret...
function ExitStatus (line 30) | function ExitStatus(status){this.name="ExitStatus";this.message="Program...
function run (line 30) | function run(args){args=args||arguments_;if(runDependencies>0){return}pr...
function Range (line 30) | function Range(start,end){this.start=typeof start==="undefined"?0:start;...
function Point (line 30) | function Point(x,y){this.x=typeof x==="undefined"?0:x;this.y=typeof y===...
function Size (line 30) | function Size(width,height){this.width=typeof width==="undefined"?0:widt...
function Rect (line 30) | function Rect(){switch(arguments.length){case 0:{this.x=0;this.y=0;this....
function RotatedRect (line 30) | function RotatedRect(){switch(arguments.length){case 0:{this.center={x:0...
function Scalar (line 30) | function Scalar(v0,v1,v2,v3){this.push(typeof v0==="undefined"?0:v0);thi...
function MinMaxLoc (line 30) | function MinMaxLoc(){switch(arguments.length){case 0:{this.minVal=0;this...
function Circle (line 30) | function Circle(){switch(arguments.length){case 0:{this.center=new Point...
function TermCriteria (line 30) | function TermCriteria(){switch(arguments.length){case 0:{this.type=0;thi...
FILE: article/node/opencv-getface.js/example/utils.js
function Utils (line 1) | function Utils(errorOutputId) { // eslint-disable-line no-unused-vars
FILE: article/node/whyheighqps/TestServlet.java
class TestServlet (line 10) | @WebServlet("/")
method doGet (line 13) | public void doGet(HttpServletRequest request, HttpServletResponse resp...
FILE: article/node/whyheighqps/test.js
function nodeSleep (line 4) | function nodeSleep(time) {
function getUnseData (line 11) | function getUnseData() {
FILE: article/serverless/first-use/test.js
function fibo (line 4) | function fibo(num)
FILE: article/v8/add_http/http.cc
function startup (line 14) | int startup(u_short *port)
function get_line (line 46) | size_t get_line(intptr_t sock, char *buf, size_t size)
function sendWithRN (line 83) | void sendWithRN(int client_num,const char* data)
function respSetHeaders (line 91) | void respSetHeaders(
function hellohttp (line 112) | void hellohttp(intptr_t client,
function accept_request (line 136) | void accept_request(void *arg,
function ListenPort (line 230) | void ListenPort(const v8::FunctionCallbackInfo<v8::Value>& args) {
function Http (line 266) | void Http(const v8::FunctionCallbackInfo<v8::Value>& args) {
FILE: article/v8/add_http/zy_node.cc
function main (line 23) | int main(int argc, char* argv[]) {
function CreateContext (line 59) | v8::Local<v8::Context> CreateContext(v8::Isolate* isolate) {
function ReadFile (line 73) | void ReadFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
function ReadCommandFile (line 99) | v8::MaybeLocal<v8::String> ReadCommandFile(v8::Isolate* isolate, const c...
function RunMain (line 123) | int RunMain(v8::Isolate* isolate, v8::Platform* platform, int argc,
function ExecuteString (line 141) | bool ExecuteString(v8::Isolate* isolate, v8::Local<v8::String> source,
function ReportException (line 176) | void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
FILE: article/v8/run_js/zy_node.cc
function main (line 22) | int main(int argc, char* argv[]) {
function CreateContext (line 57) | v8::Local<v8::Context> CreateContext(v8::Isolate* isolate) {
function ReadFile (line 67) | void ReadFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
function ReadCommandFile (line 92) | v8::MaybeLocal<v8::String> ReadCommandFile(v8::Isolate* isolate, const c...
function RunMain (line 116) | int RunMain(v8::Isolate* isolate, v8::Platform* platform, int argc,
function ExecuteString (line 133) | bool ExecuteString(v8::Isolate* isolate, v8::Local<v8::String> source,
function ReportException (line 168) | void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
FILE: article/wasm/emscripten-calling-c/add.c
function EMSCRIPTEN_KEEPALIVE (line 3) | EMSCRIPTEN_KEEPALIVE
FILE: article/wasm/emscripten-calling-c/add.js
function locateFile (line 69) | function locateFile(path) {
function staticAlloc (line 251) | function staticAlloc(size) {
function dynamicAlloc (line 259) | function dynamicAlloc(size) {
function alignMemory (line 274) | function alignMemory(size, factor) {
function getNativeTypeSize (line 280) | function getNativeTypeSize(type) {
function warnOnce (line 302) | function warnOnce(text) {
function addFunction (line 325) | function addFunction(func, sig) {
function removeFunction (line 339) | function removeFunction(index) {
function getFuncWrapper (line 345) | function getFuncWrapper(func, sig) {
function makeBigInt (line 373) | function makeBigInt(low, high, unsigned) {
function dynCall (line 377) | function dynCall(sig, ptr, args) {
function getCompilerSetting (line 390) | function getCompilerSetting(name) {
function assert (line 438) | function assert(condition, text) {
function getCFunc (line 447) | function getCFunc(ident) {
function ccall (line 488) | function ccall(ident, returnType, argTypes, args, opts) {
function cwrap (line 516) | function cwrap(ident, returnType, argTypes, opts) {
function setValue (line 523) | function setValue(ptr, value, type, noSafe) {
function getValue (line 539) | function getValue(ptr, type, noSafe) {
function allocate (line 575) | function allocate(slab, types, allocator, ptr) {
function getMemory (line 645) | function getMemory(size) {
function Pointer_stringify (line 652) | function Pointer_stringify(ptr, length) {
function AsciiToString (line 687) | function AsciiToString(ptr) {
function stringToAscii (line 699) | function stringToAscii(str, outPtr) {
function UTF8ArrayToString (line 707) | function UTF8ArrayToString(u8Array, idx) {
function UTF8ToString (line 759) | function UTF8ToString(ptr) {
function stringToUTF8Array (line 776) | function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) {
function stringToUTF8 (line 836) | function stringToUTF8(str, outPtr, maxBytesToWrite) {
function lengthBytesUTF8 (line 843) | function lengthBytesUTF8(str) {
function UTF16ToString (line 871) | function UTF16ToString(ptr) {
function stringToUTF16 (line 907) | function stringToUTF16(str, outPtr, maxBytesToWrite) {
function lengthBytesUTF16 (line 931) | function lengthBytesUTF16(str) {
function UTF32ToString (line 935) | function UTF32ToString(ptr) {
function stringToUTF32 (line 967) | function stringToUTF32(str, outPtr, maxBytesToWrite) {
function lengthBytesUTF32 (line 996) | function lengthBytesUTF32(str) {
function allocateUTF8 (line 1011) | function allocateUTF8(str) {
function allocateUTF8OnStack (line 1019) | function allocateUTF8OnStack(str) {
function demangle (line 1026) | function demangle(func) {
function demangleAll (line 1031) | function demangleAll(text) {
function jsStackTrace (line 1041) | function jsStackTrace() {
function stackTrace (line 1058) | function stackTrace() {
function alignUp (line 1071) | function alignUp(x, multiple) {
function updateGlobalBuffer (line 1098) | function updateGlobalBuffer(buf) {
function updateGlobalBufferViews (line 1102) | function updateGlobalBufferViews() {
function writeStackCookie (line 1122) | function writeStackCookie() {
function checkStackCookie (line 1128) | function checkStackCookie() {
function abortStackOverflow (line 1136) | function abortStackOverflow(allocSize) {
function abortOnCannotGrowMemory (line 1141) | function abortOnCannotGrowMemory() {
function enlargeMemory (line 1146) | function enlargeMemory() {
function getTotalMemory (line 1182) | function getTotalMemory() {
function callRuntimeCallbacks (line 1191) | function callRuntimeCallbacks(callbacks) {
function preRun (line 1221) | function preRun() {
function ensureInitRuntime (line 1232) | function ensureInitRuntime() {
function preMain (line 1239) | function preMain() {
function exitRuntime (line 1244) | function exitRuntime() {
function postRun (line 1250) | function postRun() {
function addOnPreRun (line 1262) | function addOnPreRun(cb) {
function addOnInit (line 1266) | function addOnInit(cb) {
function addOnPreMain (line 1270) | function addOnPreMain(cb) {
function addOnExit (line 1274) | function addOnExit(cb) {
function addOnPostRun (line 1278) | function addOnPostRun(cb) {
function writeStringToMemory (line 1287) | function writeStringToMemory(string, buffer, dontAddNull) {
function writeArrayToMemory (line 1302) | function writeArrayToMemory(array, buffer) {
function writeAsciiToMemory (line 1307) | function writeAsciiToMemory(str, buffer, dontAddNull) {
function unSign (line 1316) | function unSign(value, bits, ignore) {
function reSign (line 1323) | function reSign(value, bits, ignore) {
function getUniqueRunDependency (line 1373) | function getUniqueRunDependency(id) {
function addRunDependency (line 1382) | function addRunDependency(id) {
function removeRunDependency (line 1416) | function removeRunDependency(id) {
function isDataURI (line 1474) | function isDataURI(filename) {
function integrateWasmJS (line 1483) | function integrateWasmJS() {
function copyTempFloat (line 1785) | function copyTempFloat(ptr) { // functions, because inlining this code i...
function copyTempDouble (line 1797) | function copyTempDouble(ptr) {
function ___lock (line 1820) | function ___lock() {}
function ___syscall140 (line 1837) | function ___syscall140(which, varargs) {SYSCALLS.varargs = varargs;
function flush_NO_FILESYSTEM (line 1854) | function flush_NO_FILESYSTEM() {
function ___syscall146 (line 1863) | function ___syscall146(which, varargs) {SYSCALLS.varargs = varargs;
function ___syscall54 (line 1897) | function ___syscall54(which, varargs) {SYSCALLS.varargs = varargs;
function ___syscall6 (line 1907) | function ___syscall6(which, varargs) {SYSCALLS.varargs = varargs;
function ___unlock (line 1919) | function ___unlock() {}
function _emscripten_memcpy_big (line 1922) | function _emscripten_memcpy_big(dest, src, num) {
function ___setErrNo (line 1930) | function ___setErrNo(value) {
function intArrayFromString (line 1952) | function intArrayFromString(stringy, dontAddNull, length) {
function intArrayToString (line 1960) | function intArrayToString(array) {
function nullFunc_ii (line 1977) | function nullFunc_ii(x) { err("Invalid function pointer called with sign...
function nullFunc_iiii (line 1979) | function nullFunc_iiii(x) { err("Invalid function pointer called with si...
function invoke_ii (line 1985) | function invoke_ii(index,a1) {
function invoke_iiii (line 1996) | function invoke_iiii(index,a1,a2,a3) {
function ExitStatus (line 2252) | function ExitStatus(status) {
function run (line 2274) | function run(args) {
function checkUnflushedContent (line 2320) | function checkUnflushedContent() {
function exit (line 2349) | function exit(status, implicit) {
function abort (line 2381) | function abort(what) {
Condensed preview — 205 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (9,833K chars).
[
{
"path": ".gitignore",
"chars": 1085,
"preview": "# Mac\n.DS_store\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid"
},
{
"path": "LICENSE",
"chars": 1066,
"preview": "MIT License\n\nCopyright (c) 2017 ZhangYuan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
},
{
"path": "README.md",
"chars": 4689,
"preview": "# myBlog\r\n\r\n我的历程\r\n\r\n# 目录\r\n\r\n- [Introduction](./README.md)\r\n- [人工智能](./article/ai/README.md)\r\n - [机器学习的概念简单手记(20181121)]"
},
{
"path": "SUMMARY.md",
"chars": 4674,
"preview": "# Summary\r\n\r\n- [Introduction](./README.md)\r\n- [人工智能](./article/ai/README.md)\r\n - [机器学习的概念简单手记(20181121)](./article/ai/t"
},
{
"path": "article/ai/README.md",
"chars": 60,
"preview": "# 人工智能\n* [机器学习的概念简单手记(20181121)](../ai/tfjs-learn/README.md)"
},
{
"path": "article/ai/tfjs-learn/README.md",
"chars": 6374,
"preview": "# 一个JS程序员对机器学习的概念简单手记\n为什么要学习机器学习,我认为有以下重要的三点:\n* 可缩短我们的编程时间,比如可以通过机器学习学习垃圾话样本,进行更快速更精准的垃圾话的检测\n* 普通编程方法难以解决的问题,比如用户潜在喜好和用户"
},
{
"path": "article/block/README.md",
"chars": 311,
"preview": "# 区块链\n* [Bitcoin公私钥是如何生成的(20190529)](../block/bitcoin-key-gen/README.md)\n* [觉醒js计算能力,浅谈加密学之椭圆加密算法(20190601)](../block/ec"
},
{
"path": "article/block/bitcoin-key-gen/README.md",
"chars": 3948,
"preview": "# Bitcoin公私钥是如何生成的\n原本一直都是靠比特币钱包生成公私钥的,但感觉一直不是很放心,尤其是npm包隐藏盗币代码后,一直感觉危险重重,加上货币交易所,也是存在倒闭或跑路的风险,毕竟是第三方。加之又看到node12支持了原生的Bi"
},
{
"path": "article/block/ecdsa-secp256k1/README.md",
"chars": 4643,
"preview": "# 觉醒js计算能力,浅谈加密学之椭圆加密算法\n上一节谈了[《Bitcoin公私钥是如何生成的》](https://github.com/zy445566/myBlog/blob/master/20190529bitcoin/2019052"
},
{
"path": "article/block/fitblock-white-paper/README.md",
"chars": 2637,
"preview": "# FitBlock:使用纯Node.JS实现的点对点电子现金系统\n本文提出通过Node.js实现区块链系统,并可以实现Web在线挖矿。目前系统基本开发完成,将处于测试阶段和最后准备阶段(包括多机可用性测试,文档编写,准备上帝区块,部署引导"
},
{
"path": "article/block/ipfs-base/README.md",
"chars": 1851,
"preview": "# ipfs基本介绍\n由于看到网上大部分IPFS的介绍都十分含糊不清,所以觉得IPFS急需要一个更强力的科普文来介绍IPFS。所以搬了官网的介绍来进行说明。\n\n# 前置知识\n\n用磁力链开过车的老司机都知道,只要在下载器里面输入磁力链链接,不"
},
{
"path": "article/block/publish-coin/Coin.sol",
"chars": 4229,
"preview": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.9;\ninterface IERC20 {\n // token名字\n function name() ext"
},
{
"path": "article/block/publish-coin/README.md",
"chars": 8133,
"preview": "# 在新的一年,我做了一个违背链圈的决定,把发币的教程手把手教给大家\n首先我这篇文章真不是标题党,因为区块链的课程都是相对比较贵的,包含如何发币的课程很多是几万人民币打底,加上区块链市场人才的极度缺失,想要利用区块链技术捞钱的老板只能砸重金"
},
{
"path": "article/design-pattern/README.md",
"chars": 198,
"preview": "# javascript的设计模式\n* [javascript的设计模式(20191118)](https://github.com/zy445566/design-pattern-in-javascript/)\n* [写《javascri"
},
{
"path": "article/design-pattern/design-pattern-knowledge/README.md",
"chars": 3171,
"preview": "# 写《javascript的设计模式》的一些总结\n最近复刻了一个[《javascript的设计模式》](https://github.com/zy445566/design-pattern-in-javascript/)。也再一次温习了j"
},
{
"path": "article/docker/README.md",
"chars": 88,
"preview": "# docker\n* [迫于开始使用windows,但docker还是不能少(20190808)](../docker/wsl-remote-docker/README.md)"
},
{
"path": "article/docker/wsl-remote-docker/README.md",
"chars": 1612,
"preview": "# 迫于开始使用windows,但docker还是不能少\n由于本人更换公司且公司统一使用windows所以开始使用windows电脑,但unix命令行用习惯了,所以再让我重新用cmd还是powerShell想想还是不太舒服,所以转战wsl("
},
{
"path": "article/front/README.md",
"chars": 164,
"preview": "# 前端\n* [全栈(gan)工程师第一步:搭建一个全栈(gan)框架(20190824)](../front/make-full-stack-framework/README.md)\n* [为什么我们还要使用前端框架来构建页面?(2019"
},
{
"path": "article/front/make-full-stack-framework/README.md",
"chars": 4583,
"preview": "# 全栈(gan)工程师第一步:搭建一个全栈(gan)框架\n作为一个全栈(gan)工程师是不是很苦恼,在一个全栈(gan)项目中被主流前后端项目所迫害所以只能多仓库开发,甚至于启动前端一个命令,启动后端一个命令。\n\n现在发挥node.js全"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/.dockerignore",
"chars": 111,
"preview": "# Dependency directories\nnode_modules/\n\n# dotenv environment variables file\nenv.js\n\n# project\nwebpack.bundle.js"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/.gitignore",
"chars": 944,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directo"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/Dockerfile",
"chars": 524,
"preview": "FROM centos:latest\n\n# 安装依赖\nRUN yum -y update \nRUN yum -y install wget\n\n# 安装node环境\nENV NODE_VERSION v12.8.0\nRUN mkdir -p"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/README.md",
"chars": 110,
"preview": "# RUN\n```sh\ncd 20190824fullstack/20190824make-full-stack-framework/full-stack-demo\nnpm install\nnpm run dev\n```"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/back/router.js",
"chars": 178,
"preview": "const HelloController = require('./src/controller/HelloController');\nmodule.exports = {\n prefixPath:'/api',\n route"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/back/src/controller/HelloController.js",
"chars": 119,
"preview": "class HelloController {\n async world(ctx) {\n return 'World';\n }\n}\n\nmodule.exports = new HelloController();"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/back/static.js",
"chars": 223,
"preview": "const path = require('path');\nconst config = require('../config');\nmodule.exports = [\n {\n urlPrefix:'/',\n "
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/config.js",
"chars": 86,
"preview": "module.exports = {\n hostname:'127.0.0.1',\n port:3000,\n appDirname:__dirname\n}"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/front/run.js",
"chars": 693,
"preview": "const config = require('../config');\nconst webpack = require('webpack');\nconst compiler = webpack(require('./webpack.con"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/front/src/index.jsx",
"chars": 566,
"preview": "\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nclass App extends React.Component {\n constructor(props) {"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/front/static/index.html",
"chars": 182,
"preview": "<!DOCTYPE HTML>\n<html>\n <head>\n <title>全栈(gan)demo</title>\n </head>\n<body>\n<div id=\"root\"></div>\n<script ty"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/front/webpack.config.js",
"chars": 559,
"preview": "const path = require('path');\nmodule.exports = {\n mode:'development',\n entry: './src/index.jsx',\n output: {\n path:"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/middleware/application-type.js",
"chars": 11219,
"preview": "module.exports = {\n \".*\": \"application/octet-stream\",\n \".pdf\": \"application/pdf\",\n \".ai\": \"application/postscri"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/middleware/router.js",
"chars": 792,
"preview": "const url = require('url');\nmodule.exports = function(router) {\n return async (ctx, next) => {\n await next();\n"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/app/middleware/static.js",
"chars": 1349,
"preview": "const path = require('path');\nconst applicationType = require('./application-type.js');\nconst fs = require('fs');\nfuncti"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/main.js",
"chars": 500,
"preview": "const Koa = require('koa');\nconst path = require('path');\nconst config = require('./app/config');\n\n/* 为全干定制的中间件 */\nconst"
},
{
"path": "article/front/make-full-stack-framework/full-stack-demo/package.json",
"chars": 691,
"preview": "{\n \"name\": \"full-stack-demo\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"dev\":"
},
{
"path": "article/front/whyneedframework/README.md",
"chars": 5089,
"preview": "# 为什么我们还要使用前端框架来构建页面?\n今天一个同事说现在前端一定要使用前端框架,现在复杂应用不用就写不下去。我说我们很多场景下未必就一定需要前端框架,很多时候没有框架反而会更好。并且我相信随着Web规范的一步一步完善,去框架化的时代或"
},
{
"path": "article/front/whyneedframework/example/README.md",
"chars": 96,
"preview": "# example\n```sh\ncd 20191023front/20191023whyneedframework/example/\nnpm install\nnpm run start\n```"
},
{
"path": "article/front/whyneedframework/example/app.js",
"chars": 1226,
"preview": "\nimport './element-registry.js';\nclass AppContainer extends HTMLElement {\n constructor() {\n super();\n c"
},
{
"path": "article/front/whyneedframework/example/components/AddList.js",
"chars": 1140,
"preview": "export default class AddList extends HTMLElement {\n constructor() {\n super();\n const template = documen"
},
{
"path": "article/front/whyneedframework/example/components/DataBind.js",
"chars": 1360,
"preview": "export default class DataBind extends HTMLElement {\n constructor() {\n super();\n const template = docume"
},
{
"path": "article/front/whyneedframework/example/components/MyBrowseRoute.js",
"chars": 574,
"preview": "export default class MyBrowseRoute extends HTMLElement {\n constructor() {\n super();\n const template = d"
},
{
"path": "article/front/whyneedframework/example/components/MyRouter.js",
"chars": 98,
"preview": "export default class MyRouter extends HTMLElement {\n constructor() {\n super();\n }\n }"
},
{
"path": "article/front/whyneedframework/example/element-registry.js",
"chars": 425,
"preview": "import MyRouter from './components/MyRouter.js';\nimport MyBrowseRoute from './components/MyBrowseRoute.js';\nimport AddLi"
},
{
"path": "article/front/whyneedframework/example/index.html",
"chars": 173,
"preview": "<!DOCTYPE HTML>\n<html>\n <head>\n <title>show time</title>\n </head>\n<body>\n <app-container />\n <script "
},
{
"path": "article/front/whyneedframework/example/package.json",
"chars": 229,
"preview": "{\n \"name\": \"example\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"app.js\",\n \"scripts\": {\n \"start\": \"npx s"
},
{
"path": "article/git/README.md",
"chars": 115,
"preview": "# git\n* [github假面技术(20180929)](../git/git-fake/README.md)\n* [如何解决多仓库项目的使用问题(20210525)](../git/gitmodules/README.md)"
},
{
"path": "article/git/git-fake/README.md",
"chars": 487,
"preview": "# 让大佬向你仓库PR关键技术,拿去装逼,不要谢我\n前段时间看到可以通过修改git的邮箱实现,实现伪造记录,但是修改起来还挺麻烦的,大佬的账号不能在命令行中保存,遂做了一个命令行工具,专门用来伪造github的提交请求。😎\n\n# 先看效果\n"
},
{
"path": "article/git/gitmodules/README.md",
"chars": 1631,
"preview": "# 如何解决多仓库项目的使用问题\n最近接手一个老项目,刚刚准备git clone的时候,我人傻了,这个项目大概有十几个子仓库,之前维护的同学告诉我让我一个仓库一个仓库自行git clone下来,然后在依次对每个项目安装依赖,最后放到指定的目"
},
{
"path": "article/http/README.md",
"chars": 152,
"preview": "# web\n* [为什么在HTTP的chunked模式下不需要设置长度(20190515)](../http/http-chunked/README.md)\n* [使用nodejs实现服务端websocket通讯(20200409)](.."
},
{
"path": "article/http/http-chunked/README.md",
"chars": 2982,
"preview": "# 为什么在HTTP的chunked模式下不需要设置长度\n昨天看到论坛上有位同学提问,为什么Transfer-Encoding为chunked的时候不需要设置content-length?那为什么非要设置content-length呢?这位"
},
{
"path": "article/http/node-web-socket/README.md",
"chars": 6830,
"preview": "# 使用nodejs实现服务端websocket通讯\n起因是在写一个前置监控服务项目,需要数据相对实时的传输,然后正好看到nodejs文档中,实现websocket看起来挺简单的(其实只是冰山一角还有坑),所以就打算自己实现一遍websoc"
},
{
"path": "article/leetcode/README.md",
"chars": 407,
"preview": "# 算法\n* [动态规划包quick-dp的拆解和使用(20180531)](../leetcode/dynamic-programming/README.md)\n* [三数之和的优化之旅(20180926)](../leetcode/th"
},
{
"path": "article/leetcode/best-time-to-buy-and-sell-stock/README.md",
"chars": 4007,
"preview": "# 动态规划难?读完这篇还不理解那就不要请我吃鸡了\n昨天同事说动态规划很难,我说不会啊,理解了就很简单,我同事表示不屑,以为我在炫技。于是乎我问了一个工作六年的前同事,他居然也觉得高大上,并且表示接触过会动态规划的朋友,觉得很牛逼。\n\n我了"
},
{
"path": "article/leetcode/best-time-to-buy-and-sell-stock/best-time-to-buy-and-sell-stock.js",
"chars": 1095,
"preview": "/**\n * @param {number[]} prices\n * @return {number}\n */\nvar maxProfit = function(prices) {\n // 优化头\n while (prices."
},
{
"path": "article/leetcode/bloom-filter/README.md",
"chars": 1940,
"preview": "# 一道算法题引发布隆过滤器原理的思考\n## 起因\n周会组内有一位同事L分享了一道算法题,题目是这样的:\n```txt\nA 文件有 50 亿条 URL,B 文件也有 50 亿条 URL,每条 URL 大小为 64B,在一台只有 4G 内存的"
},
{
"path": "article/leetcode/build-tree/README.md",
"chars": 1477,
"preview": "# 分享一个构建树的方法\n## 起因\n想不到在2024年,我居然会重新开始刷题,但是作为JavaScript开发,在刷到树的时候,还是特别烦恼,倒也不是因为难而烦恼,而是因为刷题的时候要调试树的结构而烦恼,所以自己写了个方法来进行数的构建。"
},
{
"path": "article/leetcode/build-tree/code/list.js",
"chars": 406,
"preview": "function ListNode(val, next) {\n this.val = (val===undefined ? 0 : val)\n this.next = (next===undefined ? null : nex"
},
{
"path": "article/leetcode/build-tree/code/tree.js",
"chars": 858,
"preview": "function TreeNode(val, left, right) {\n this.val = (val===undefined ? 0 : val)\n this.left = (left===undefined ? nul"
},
{
"path": "article/leetcode/dynamic-programming/README.md",
"chars": 5207,
"preview": "# 动态规划包quick-dp的拆解和使用\n### 什么是动态规划\n动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E"
},
{
"path": "article/leetcode/sudoku-solver/README.md",
"chars": 6849,
"preview": "# 解数独\n刚刷完《有效的数独》后就刷了《解数独》这道题。刷完后看到这道题被标记成`困难`,并且感觉还挺有意思的,所以正好拿出来分享一下。\n\n# 先看看数独游戏的规则吧(玩过的可以忽略)\n1. 数字 1-9 在每一行只能出现一次。\n2. 数"
},
{
"path": "article/leetcode/sudoku-solver/sudokuSolver.js",
"chars": 2994,
"preview": "/**\n * @param {character[][]} board\n * @return {void} \n */\n// github@zy445566\nvar solveSudoku = function(board) {\n fu"
},
{
"path": "article/leetcode/three-sum/README.md",
"chars": 6460,
"preview": "# 三数之和的优化之旅\n最近一期的996结束,准备在空闲的时间学点什么,就在纠结的时候看到leetcode中国版推出了,正好提升一下自身的算法能力,遂开始刷题。可以说从中有一定提升,但大部分还是暴力刷题过的。这次下班回家刷三数之和的时候感觉"
},
{
"path": "article/leetcode/three-sum/package.json",
"chars": 508,
"preview": "{\n \"name\": \"three-sum\",\n \"version\": \"1.0.0\",\n \"description\": \"我的历程\",\n \"main\": \"treeSumBenchmark.js\",\n \"scripts\": {\n"
},
{
"path": "article/leetcode/three-sum/treeSumBenchmark.js",
"chars": 24213,
"preview": "var testData= [-99927,-99851,-99822,-99637,-99583,-99555,-99525,-99524,-99508,-99500,-99486,-99468,-99280,-99233,-99156,"
},
{
"path": "article/linux/README.md",
"chars": 148,
"preview": "# linux\n* [教你一步一步利用rsyslog搭建自己的日志系统(20170808)](../linux/rsyslog/README.md)\n* [警惕Linux发行版危机和即将或已经发生的重大影响(20230221)](../li"
},
{
"path": "article/linux/centos-danger/README.md",
"chars": 2805,
"preview": "# 警惕Linux发行版危机和即将或已经发生的重大影响\r\n起因是Node.js的开发者提出要升级Node.js镜像版本,原因是Node.js16由于OpenSSL 1.1.1停止维护即将于今年9月份提前停止维护,具体可参考[Node.js1"
},
{
"path": "article/linux/rsyslog/README.md",
"chars": 5367,
"preview": "# 教你一步一步利用rsyslog搭建自己的日志系统\n## rsys是什么\nrsyslog 是一个syslog的升级版,可用于处理大型的系统的日志需求。并且可以把输入到日志转换到各个数据系统上,如图。\n](../llvm/jsvm_c/README.md)\n* [使用JS实现JS编译器,并将目标js生成二进制(20180908)](../llvm/jsvm"
},
{
"path": "article/llvm/how_to_ml/README.md",
"chars": 1647,
"preview": "# 如何自己创建一种编程语言\n正好前段时间做了几个JS语言编译器的DEMO,我觉得我可以说说作为一个野路子是如何用LLVM实现语言的。这个还是比较初级的,大佬可以忽略,但或许可以帮助一些刚刚入门想要用LLVM实现语言的朋友。\n\n# 照老虎画"
},
{
"path": "article/llvm/jsvm_c/README.md",
"chars": 10564,
"preview": "# 利用LLVM实现JS的编译器,创造属于自己的语言\n本文参考了官方教程Kaleidoscope语言的实现,本文只实现了JS的编译器的demo,如果想要加深学习比如语言的JIT的实现和语言的代码优化,我将官方教程和代码集合打包在了 [git"
},
{
"path": "article/llvm/jsvm_c/fibo.js",
"chars": 135,
"preview": "// fibo.js 这是斐波纳切数\nfunction fibo(num) {\n if (num<3) {\n return 1;\n } else {\n return fibo(num-1)+fibo("
},
{
"path": "article/llvm/jsvm_c/jsvm.cpp",
"chars": 8086,
"preview": "#include \"jsvm.h\"\n\n// clang++ -g -O3 jsvm.cpp `llvm-config --cxxflags --ldflags --system-libs --libs all` -o jsvm\n// ./"
},
{
"path": "article/llvm/jsvm_c/jsvm.h",
"chars": 10875,
"preview": "#include \"llvm/ADT/APFloat.h\"\n#include \"llvm/ADT/Optional.h\"\n#include \"llvm/ADT/STLExtras.h\"\n#include \"llvm/IR/BasicBloc"
},
{
"path": "article/llvm/jsvm_c/main.cpp",
"chars": 145,
"preview": "// main.cpp\n#include <iostream>\n\nextern \"C\" {\n double fibo(double);\n}\n\nint main() {\n std::cout << \"fibo(9) is: \" <"
},
{
"path": "article/llvm/jsvm_js/.vscode/settings.json",
"chars": 369,
"preview": "{\n \"files.associations\": {\n \"__config\": \"cpp\",\n \"__nullptr\": \"cpp\",\n \"cstddef\": \"cpp\",\n \""
},
{
"path": "article/llvm/jsvm_js/README.md",
"chars": 8411,
"preview": "# 使用JS实现JS编译器,并将目标js生成二进制\n上一篇文章 [利用LLVM实现JS的编译器,创造属于自己的语言](https://github.com/zy445566/myBlog/tree/master/20180825llvm/2"
},
{
"path": "article/llvm/jsvm_js/fibo.js",
"chars": 159,
"preview": "// fibo.js 这是斐波纳切数\nfunction fibo(num) {\n if (num<=2) {return 1;}\n return fibo(num-1)+fibo(num-2);\n}\n\nfunction main"
},
{
"path": "article/llvm/jsvm_js/fibo.s",
"chars": 2081,
"preview": "\t.section\t__TEXT,__text,regular,pure_instructions\n\t.macosx_version_min 10, 13\n\t.section\t__TEXT,__literal8,8byte_literals"
},
{
"path": "article/llvm/jsvm_js/index.js",
"chars": 599,
"preview": "\nconst parse = require('./lib/parse');\nconst jsvm = require('./lib/jsvm');\nconst path = require('path');\n\n// node ind"
},
{
"path": "article/llvm/jsvm_js/lib/jsvm.js",
"chars": 8929,
"preview": "const llvm = require('llvm-node');\nconst the_context = new llvm.LLVMContext();\nconst the_module = new llvm.Module(\"jsvm"
},
{
"path": "article/llvm/jsvm_js/lib/parse.js",
"chars": 220,
"preview": "const babel_core = require('@babel/core');\nconst fs = require('fs');\nmodule.exports = function (js_path){\n let js_co"
},
{
"path": "article/llvm/jsvm_js/package.json",
"chars": 428,
"preview": "{\n \"name\": \"llvm_js\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo"
},
{
"path": "article/llvm/jsvm_js/printDouble.cpp",
"chars": 303,
"preview": "// printDouble.cpp\n#include <stdio.h>\n// 问什么要要加extern \"C\" ,因为c++编译的时候会自动进行函数签名\n// 如果没有extern \"C\" ,汇编里的方法名就会是Z11printDoub"
},
{
"path": "article/llvm/jsvm_js/printDouble.s",
"chars": 870,
"preview": "\t.section\t__TEXT,__text,regular,pure_instructions\n\t.macosx_version_min 10, 13\n\t.globl\t_printDouble ## -- Begi"
},
{
"path": "article/node/README.md",
"chars": 1148,
"preview": "# node\r\n\r\n- [教你一步一步使用 rust 写 js 扩展(20170721)](../node/node-rust-bindings/README.md)\r\n- [nodejs 面向指针编程(20180402)](../node"
},
{
"path": "article/node/block-run/README.md",
"chars": 6825,
"preview": "# 针对使用非块运行和块运行并发压测对比\n\n### 为什么会有这个实验\n由于cnode上的一篇提问 [node.js单线程,是不是就不用消息队列了啊?](https://cnodejs.org/topic/5acc702a042a804dc"
},
{
"path": "article/node/block-run/index.js",
"chars": 5324,
"preview": "const http = require('http');\nconst BlockRun = require('block-run');\nconst url = require('url');\nconst mysql = require('"
},
{
"path": "article/node/block-run/package.json",
"chars": 404,
"preview": "{\n \"name\": \"20180414block-run\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"te"
},
{
"path": "article/node/block-run/test.sql",
"chars": 363,
"preview": "CREATE DATABASE IF NOT EXISTS `test`;\nCREATE TABLE IF NOT EXISTS `test`.`test` (\n `id` int(11) unsigned NOT NULL AUTO_"
},
{
"path": "article/node/danger-arguments/README.md",
"chars": 1715,
"preview": "# 警惕JS数组解构转参数导致爆栈问题\n起源是在写webscoket服务的时候,发现开发工具偶尔报`Maximum call stack size exceeded`的问题。由于当时没时间,就草草把[BUG](https://github."
},
{
"path": "article/node/deno/README.md",
"chars": 186,
"preview": "# deno\n* [编译deno,deno结构解析(20190125)](../deno/build/README.md)\n* [deno系列第二篇,给deno做rust扩展(20190201)](../deno/exends/README"
},
{
"path": "article/node/deno/build/README.md",
"chars": 2680,
"preview": "# 编译deno,deno结构解析\ndeno原本在我不被重视技术列表中,但在github的年度开源star榜单上,斩获年度第四。让我不得不思考为什么deno能广受好评,我想我需要刨开deno看看了。\n\n# 编译\n为什么不能直接解刨,呃。。。"
},
{
"path": "article/node/deno/exends/README.md",
"chars": 4235,
"preview": "# deno系列第二篇,给deno做rust扩展\n这篇文章主要接着 [《编译deno,deno结构解析》](https://github.com/zy445566/myBlog/blob/master/20190125deno/201901"
},
{
"path": "article/node/deno/frame/README.md",
"chars": 5830,
"preview": "# 如何构建一个同时支持node和deno的框架\n这篇文章,我更倾向于在分享自己的对构建多运行时框架构建思想,同时希望能够听取更多人的想法,构建更好的过渡框架。\n\n# 最初想法\n最初想法是收到wine的启发,直接用deno实现一套node的"
},
{
"path": "article/node/deno/frame/jsFrame/README.md",
"chars": 175,
"preview": "# jsFrame\n一个可以使用node和deno运行的Web框架(DEMO版本)\n# run \n```sh\n# 使用node运行框架(注意:node版本为11以上,12也可能会存在API变更,nvm use 11.5 )\nsh run.s"
},
{
"path": "article/node/deno/frame/jsFrame/config.js",
"chars": 35,
"preview": "export default {\n 'port':3000,\n}"
},
{
"path": "article/node/deno/frame/jsFrame/controller/HelloController.js",
"chars": 237,
"preview": "import HelloServer from '../server/HelloServer.js'\nexport default class HelloController {\n constructor() {\n th"
},
{
"path": "article/node/deno/frame/jsFrame/engineMiddle/deno/file.js",
"chars": 596,
"preview": "import { open } from \"deno\";\nexport default class FileManger {\n static async getContent(url,filePath) {\n let u"
},
{
"path": "article/node/deno/frame/jsFrame/engineMiddle/deno/http.js",
"chars": 833,
"preview": "import { serve } from \"https://deno.land/x/std@v0.2.10/http/server.ts\";\nexport default class Http {\n constructor(rout"
},
{
"path": "article/node/deno/frame/jsFrame/engineMiddle/deno/mod.js",
"chars": 177,
"preview": "import Http from './http.js';\nimport File from './file.js';\nimport main from '../../main.js';\nwindow.midInject = {\n c"
},
{
"path": "article/node/deno/frame/jsFrame/engineMiddle/node/custom-loader.mjs",
"chars": 1007,
"preview": "import path from 'path';\nimport process from 'process';\nimport Module from 'module';\n\nconst builtins = Module.builtinMod"
},
{
"path": "article/node/deno/frame/jsFrame/engineMiddle/node/file.js",
"chars": 235,
"preview": "import fs from 'fs';\nimport path from 'path';\nexport default class FileManger {\n static async getContent(url,filePath"
},
{
"path": "article/node/deno/frame/jsFrame/engineMiddle/node/http.js",
"chars": 723,
"preview": "import http from 'http';\nexport default class Http {\n constructor(router) {\n this.router = router;\n }\n\n "
},
{
"path": "article/node/deno/frame/jsFrame/engineMiddle/node/index.js",
"chars": 1457,
"preview": "import vm from 'vm';\nimport fs from 'fs';\nimport path from 'path';\nimport Http from './http.js';\nimport File from './fil"
},
{
"path": "article/node/deno/frame/jsFrame/main.js",
"chars": 273,
"preview": "import config from './config.js';\nimport Router from './router.js';\nlet mian = (async()=>{\n const Http = midInject.Ht"
},
{
"path": "article/node/deno/frame/jsFrame/router.js",
"chars": 319,
"preview": "import HelloController from './controller/HelloController.js'\nexport default class Router {\n constructor() {\n "
},
{
"path": "article/node/deno/frame/jsFrame/run.sh",
"chars": 290,
"preview": "#!/usr/bin/env bash\nif [ \"$1\" == \"deno\" ]\nthen\n deno --allow-read --allow-write --allow-net --allow-env ./engineMiddl"
},
{
"path": "article/node/deno/frame/jsFrame/server/HelloServer.js",
"chars": 266,
"preview": "export default class HelloServer {\n constructor () {\n this.File = midInject.File;\n }\n async hello() {\n "
},
{
"path": "article/node/deno/frame/jsFrame/server/db.json",
"chars": 23,
"preview": "{\"hello\":\"hello world\"}"
},
{
"path": "article/node/eventloop/README.md",
"chars": 2112,
"preview": "# 又被node的eventloop坑了,这次是node的锅\n近日在论坛上看到一篇文章讲node和谷歌浏览器的eventloop的区别,因为看到写的还不错,我表示了肯定。但没过多久一位坛友却说node11结果不一样,我说怎么可能不一样。接着"
},
{
"path": "article/node/left-shift-operator-keng/README.md",
"chars": 1789,
"preview": "# 警惕位移运算<<的坑\n在一次代码优化的过程中把\n```js\n// a,b都为正整数且大于0\nwhile (a>=b) {\n a-=b;\n}\n```\n优化为\n```js\n// a,b都为正整数且大于0\nwhile (a>=b) {\n"
},
{
"path": "article/node/memory-leak/README.md",
"chars": 218,
"preview": "# 内存泄露\n* [实战线上内存泄漏问题(20190515)](../memory-leak/actual/README.md)\n* [利用vm解决复杂的内存泄漏问题(20200628)](../memory-leak/vm-save-le"
},
{
"path": "article/node/memory-leak/actual/README.md",
"chars": 3465,
"preview": "# 实战线上内存泄漏问题\n### 故事背景\n由于我们线上验证码突然出现假死,记录最后一次node崩溃的线上node内存使用达到了1.36GB,所以基本断定是内存泄漏了\n\n### 工具\nheapdump(npm包)\nchrome浏览器中的开发"
},
{
"path": "article/node/memory-leak/require-vm/README.md",
"chars": 3712,
"preview": "# 利用require-vm实现一个更安全的引用,更好的防止内存泄漏篇\n之前由于遇到了发现泄漏变量,但是代码结构庞大无法直接修改。就好像你引用了别人一个庞大的库,而且这个库有10000多个内存泄漏的方法等着你去修复,但这任务量无疑是庞大的。"
},
{
"path": "article/node/memory-leak/require-vm/case1-require-vm.js",
"chars": 89,
"preview": "const requireVM = require('require-vm');\nwhile(true) {\n requireVM('./case1.js').leak()\n}"
},
{
"path": "article/node/memory-leak/require-vm/case1-require.js",
"chars": 46,
"preview": "while(true) {\n require('./case1.js').leak()\n}"
},
{
"path": "article/node/memory-leak/require-vm/case1.js",
"chars": 100,
"preview": "var leakArray = []; \nexports.leak = function () { \n leakArray.push(\"leak\" + Math.random()); \n};"
},
{
"path": "article/node/memory-leak/require-vm/case2-require-vm.js",
"chars": 325,
"preview": "const requireVM = require('require-vm');\nfunction getN (n) {\n return n;\n};\nlet memoize = requireVM('lodash').memoize\nle"
},
{
"path": "article/node/memory-leak/require-vm/case2-require.js",
"chars": 158,
"preview": "function getN (n) {\n return n;\n};\nlet memoize = require('lodash').memoize\nlet memoizeGetN = memoize(getN);\nlet i=0n;\nwh"
},
{
"path": "article/node/memory-leak/require-vm/case3.js",
"chars": 179,
"preview": "// case3.js\n var circle = require('./circle.js');\n console.log('The area of a circle of radius 4 is ' + circle.area(4)"
},
{
"path": "article/node/memory-leak/require-vm/package.json",
"chars": 308,
"preview": "{\n \"name\": \"20200628require-vm\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"case1-require.js\",\n \"scripts\": "
},
{
"path": "article/node/memory-leak/vm-save-leak/README.md",
"chars": 2072,
"preview": "# 利用vm解决复杂的内存泄漏问题(Emscripten踩坑之路)\n前段时间移植了一些项目到Wasm上,在公司项目上用户还没发现问题(可能是PM2不断奶活吧),反倒开源项目先被github其它用户发现了存在内存泄漏问题,我一查确实存在。即使"
},
{
"path": "article/node/mmap/README.md",
"chars": 2960,
"preview": "# 如何使用node.js实现内存共享\n这篇文章的起因是因为一道node.js面试题,面试官问“进程通讯有哪些方式?”,我说我就只记得socket和信号量了。他说少了,还有一种内存共享叫 “内存映射”,我在想不会是mmap吧。但想了想万一装"
},
{
"path": "article/node/mmap/share-object/README.md",
"chars": 241,
"preview": "# build\n```sh\ncd 20190115node/20191107mmap/share-object\nnode-gyp build\n```\n# run\n第一个终端运行: \n```sh\ncd 20190115node/2019110"
},
{
"path": "article/node/mmap/share-object/binding.gyp",
"chars": 108,
"preview": "{\n \"targets\": [\n {\n \"target_name\": \"shareObject\",\n \"sources\": [ \"shareObject.cc\" ]\n }\n ]\n}"
},
{
"path": "article/node/mmap/share-object/build/Makefile",
"chars": 13557,
"preview": "# We borrow heavily from the kernel build setup, though we are simpler since\n# we don't have Kconfig tweaking settings o"
},
{
"path": "article/node/mmap/share-object/build/Release/.deps/Release/obj.target/shareObject/shareObject.o.d",
"chars": 1821,
"preview": "cmd_Release/obj.target/shareObject/shareObject.o := c++ '-DNODE_GYP_MODULE_NAME=shareObject' '-DUSING_UV_SHARED=1' '-DUS"
},
{
"path": "article/node/mmap/share-object/build/Release/.deps/Release/shareObject.node.d",
"chars": 247,
"preview": "cmd_Release/shareObject.node := c++ -bundle -undefined dynamic_lookup -Wl,-no_pie -Wl,-search_paths_first -mmacosx-versi"
},
{
"path": "article/node/mmap/share-object/build/binding.Makefile",
"chars": 119,
"preview": "# This file is generated by gyp; do not edit.\n\nexport builddir_name ?= ./build/.\n.PHONY: all\nall:\n\t$(MAKE) shareObject\n"
},
{
"path": "article/node/mmap/share-object/build/config.gypi",
"chars": 2198,
"preview": "# Do not edit. File was generated by node-gyp's \"configure\" step\n{\n \"target_defaults\": {\n \"cflags\": [],\n \"default"
},
{
"path": "article/node/mmap/share-object/build/gyp-mac-tool",
"chars": 23476,
"preview": "#!/usr/bin/env python\n# Generated by gyp. Do not edit.\n# Copyright (c) 2012 Google Inc. All rights reserved.\n# Use of th"
},
{
"path": "article/node/mmap/share-object/build/shareObject.target.mk",
"chars": 5125,
"preview": "# This file is generated by gyp; do not edit.\n\nTOOLSET := target\nTARGET := shareObject\nDEFS_Debug := \\\n\t'-DNODE_GYP_MODU"
},
{
"path": "article/node/mmap/share-object/package.json",
"chars": 320,
"preview": "{\n \"name\": \"share-object\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": "
},
{
"path": "article/node/mmap/share-object/shareObject.cc",
"chars": 2221,
"preview": "#include <node_api.h>\n#include <assert.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include<fcntl.h>\n#include<stdio.h"
},
{
"path": "article/node/mmap/share-object/shareRead.js",
"chars": 193,
"preview": "const shareObject = require('bindings')('shareObject');\n// 初始化数据\nshareObject.write('\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0"
},
{
"path": "article/node/mmap/share-object/shareWrite.js",
"chars": 90,
"preview": "const shareObject = require('bindings')('shareObject');\nshareObject.write('Hello World!');"
},
{
"path": "article/node/multi-threaded/README.md",
"chars": 1636,
"preview": "# 如何更爽的在JS中使用多线程\n最近写多线程的时候遇到一个烦恼,就是用起来实在太麻烦,不管是WebWorker还是worker_threads库,用起来都实在太麻烦了。而且很多时候IO密集和CPU密集操作很多时候是交织的,有没有一种办法,"
},
{
"path": "article/node/ncpu-resue-threaded/README.md",
"chars": 1401,
"preview": "# 使用ncpu解锁真·多线程复用\r\n最近看了一些线程池的库,发现很多都是伪多线程复用,什么叫伪多线程复用呢?我看到这么一种情况只规定了多线程的最大数量等于CPU数量,来一个请求就启动一个线程,超出后请求就开始排队。那么这会有什么问题呢?就"
},
{
"path": "article/node/node-debugger/README.md",
"chars": 1897,
"preview": "# 使用inspect进行远程调试\r\n我们什么时候需要远程调试呢?比如在容器中,如果使用传统的方法只能cp进去修改测试环境的文件来debugger,这样做是非常麻烦的,但是如果我们对测试环境做判断,如果是测试环境我们则可以启动inspect"
},
{
"path": "article/node/node-rust-bindings/.gitignore",
"chars": 884,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directo"
},
{
"path": "article/node/node-rust-bindings/LICENSE",
"chars": 1066,
"preview": "MIT License\n\nCopyright (c) 2017 ZhangYuan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
},
{
"path": "article/node/node-rust-bindings/README.md",
"chars": 2728,
"preview": "# 教你一步一步使用rust写js扩展\n* 这是一个用于制作node的rust扩展的的中文教程,接下来我将会一步一步教学。\n* 由于天朝墙的问题,建议大家先准备好代理。\n\n## 准备\n### 如果之前使用过node-gyp则只需要安装rus"
},
{
"path": "article/node/node-rust-bindings/fib/.gitignore",
"chars": 89,
"preview": "native/target\nnative/index.node\nnative/artifacts.json\n**/*~\n**/node_modules\n**/.DS_Store\n"
},
{
"path": "article/node/node-rust-bindings/fib/README.md",
"chars": 8,
"preview": "# fib\n\n\n"
},
{
"path": "article/node/node-rust-bindings/fib/lib/index.js",
"chars": 91,
"preview": "var addon = require('../native');\n\nconsole.log(addon.hello());\nconsole.log(addon.fib(30));\n"
},
{
"path": "article/node/node-rust-bindings/fib/native/Cargo.toml",
"chars": 230,
"preview": "[package]\nname = \"fib\"\nversion = \"0.1.0\"\nauthors = [\"ZY <zyct@vip.qq.com>\"]\nlicense = \"MIT\"\nbuild = \"build.rs\"\n\n[lib]\nna"
},
{
"path": "article/node/node-rust-bindings/fib/native/build.rs",
"chars": 144,
"preview": "extern crate neon_build;\n\nfn main() {\n neon_build::setup(); // must be called in build.rs\n\n // add project-specifi"
},
{
"path": "article/node/node-rust-bindings/fib/native/src/lib.rs",
"chars": 995,
"preview": "#[macro_use]\nextern crate neon;\n\nuse neon::vm::{Call, JsResult};\nuse neon::js::JsString;\nuse neon::js::JsInteger;\nuse ne"
},
{
"path": "article/node/node-rust-bindings/fib/package.json",
"chars": 244,
"preview": "{\n \"name\": \"fib\",\n \"version\": \"0.1.0\",\n \"description\": \"\",\n \"main\": \"lib/index.js\",\n \"author\": \"ZY <zyct@vip.qq.com"
},
{
"path": "article/node/npmtaobaobye/README.md",
"chars": 1373,
"preview": "# 前端和Node端的同学注意了,在1月22日之后可能就无法编译C++扩展了\n这听起来很很吓人,但实际也是如此,虽然2024年1月22日已经过去,但是这个风险依然没有过去相信很多人也遇到了类似的问题,简单来说就是国内某宝npm镜像证书过期了"
},
{
"path": "article/node/opencv-getface.js/README.md",
"chars": 6690,
"preview": "# 使用WebAssembly版本opencv实现人脸识别\n最近公司的需求又开始作妖了,说要做用户人脸识别,要知道照片有几个脸,和脸部位置。这需求下来让我这个CURD-BOY有点慌了,果然这个重任又落到了我身上。所以开始研究扣脸技术,之前使"
},
{
"path": "article/node/opencv-getface.js/example/haarcascade_frontalface_default.xml",
"chars": 930127,
"preview": "<?xml version=\"1.0\"?>\n<!--\n Stump-based 24x24 discrete(?) adaboost frontal face detector.\n Created by Rainer Lienh"
},
{
"path": "article/node/opencv-getface.js/example/index.html",
"chars": 3039,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>Hello OpenCV.js</title>\n</head>\n<body>\n<h2>Hello OpenCV.js</"
},
{
"path": "article/node/opencv-getface.js/example/node-index.js",
"chars": 1643,
"preview": "Module = {\n async onRuntimeInitialized() {\n console.log(cv.getBuildInformation())\n await getFace()\n }\n "
},
{
"path": "article/node/opencv-getface.js/example/opencv.js",
"chars": 8164721,
"preview": "(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous mod"
},
{
"path": "article/node/opencv-getface.js/example/package.json",
"chars": 288,
"preview": "{\n \"name\": \"getface\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"node-index.js\",\n \"dependencies\": {\n \"ji"
},
{
"path": "article/node/opencv-getface.js/example/utils.js",
"chars": 5668,
"preview": "function Utils(errorOutputId) { // eslint-disable-line no-unused-vars\n let self = this;\n this.errorOutput = docume"
},
{
"path": "article/node/quickjs/README.md",
"chars": 62,
"preview": "# quickjs\n* [quickjs初体验(20191029)](../quickjs/touch/README.md)"
},
{
"path": "article/node/quickjs/touch/README.md",
"chars": 2906,
"preview": "# quickjs初体验\n第一次看到quickjs,就感觉挺有意思的,对我个人来说解决了以下问题:\n* 可以把js编译成二进制(解决js代码加密问题)\n* 足够轻量,只需要将编译好的文件扔到对应系统即可运行,不需要带运行时\n* 更纯粹的js"
},
{
"path": "article/node/stream-question/README.md",
"chars": 1845,
"preview": "# 小记 Node.js 关于文件描述符的坑\n\n在之前遇到过一个 Node.js 使用文件描述符来读取文件,未释放文件描述符的坑,对此还对 Node.js 提过 PR,让 Node.js 支持文件句柄的变量 GC 后,也同时销毁句柄,在 f"
},
{
"path": "article/node/type-pointer/README.md",
"chars": 2112,
"preview": "# nodejs面向指针编程\n### 我们可以用指针读取node数据啦\n第一步肯定是\n```sh\nnpm install type-pointer\n```\n接下来\n```js\nconst TypePointer = require('typ"
},
{
"path": "article/node/why-await-not-block/README.md",
"chars": 3153,
"preview": "# 为什么说javascript的await不是阻塞\r\n先讲讲为什么要写这篇文章,我发现很多人对async/await的理解在不同的层次,大部分人对异步模型还在一个相对模糊的概念,很多人对异步模型甚至是有许多错误的看法,我觉得有必要写一篇文"
},
{
"path": "article/node/whyheighqps/README.md",
"chars": 4189,
"preview": "# 为什么说node具有高并发优势\n很多人质疑node的高并发优势,并且以输出HelloWorld或输出计算结果来和传统的Java对比证明node并没有web的高并发优势,但事实真的是这样么?为什么说只输出HelloWorld性能还是比不过"
},
{
"path": "article/node/whyheighqps/TestServlet.java",
"chars": 1003,
"preview": "package test;\n\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.h"
},
{
"path": "article/node/whyheighqps/test.js",
"chars": 741,
"preview": "const http = require(\"http\");\nconst fs = require(\"fs\");\nconst port = 4000;\nfunction nodeSleep(time) {\n return new Pro"
},
{
"path": "article/serverless/README.md",
"chars": 157,
"preview": "# serverless\n* [Node.js的CPU密集计算的终结解决方案(20171214)](../serverless/first-use/README.md)\n* [个人搭建serverless架构快速指南(20191010)]("
},
{
"path": "article/serverless/first-use/README.md",
"chars": 3363,
"preview": "# Node.js的CPU密集计算的终结解决方案!!!(这篇文章是早期写的存在很多问题,请忽略)\n## 好吧我承认我是标题党,但这种方法确实解决了node的CPU密集型计算时造成服务无法响应或响应过慢问题,其实我讲的的是Faas的serve"
},
{
"path": "article/serverless/first-use/test.js",
"chars": 416,
"preview": "var http = require('http');\nvar url = require('url');\n\nfunction fibo(num)\n{\n\tif(num<2){return 1;} \n \treturn fibo(num-1)"
},
{
"path": "article/serverless/knative-faas/README.md",
"chars": 7513,
"preview": "# 个人搭建serverless架构快速指南\n之前我们都是基于云服务商的serverless来做服务的,但是存在一个问题,不够标准化,即每个云服务商都可能有自己的一套方案,最近在看knative,发现是一套不错的标准化方案,所以个人搭建来尝"
},
{
"path": "article/serverless/knative-faas/hello-world/.dockerignore",
"chars": 47,
"preview": "Dockerfile\nnode_modules\nnpm-debug.log\nREADME.md"
},
{
"path": "article/serverless/knative-faas/hello-world/.gitignore",
"chars": 37,
"preview": "node_packages/\nnode_modules\nREADME.md"
},
{
"path": "article/serverless/knative-faas/hello-world/Dockerfile",
"chars": 87,
"preview": "FROM node:12-slim\nWORKDIR /usr/src/app\nCOPY . ./\nRUN npm install\nCMD [ \"npm\", \"start\" ]"
},
{
"path": "article/serverless/knative-faas/hello-world/index.js",
"chars": 261,
"preview": "const http = require('http');\nconst port = process.env.PORT || 8080;\nhttp.createServer((req, res) => {\n const target "
},
{
"path": "article/serverless/knative-faas/hello-world/package.json",
"chars": 242,
"preview": "{\n \"name\": \"hello-world\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"start\": "
},
{
"path": "article/serverless/knative-faas/hello-world/service.yaml",
"chars": 283,
"preview": "apiVersion: serving.knative.dev/v1\nkind: Service\nmetadata:\n name: helloworld-nodejs\n namespace: default\nspec:\n templa"
},
{
"path": "article/serverless/knative-faas/op.md",
"chars": 3081,
"preview": "# 个人快速搭建serverless快速指南(建设中,目前只记录一些操作)\nhttps://multipass.run/#install\n```sh\n# 这里的话最好分配双核和4G内存,否则将不够,如果配置高可以适当多分配\nmultipas"
},
{
"path": "article/shadowsocks/README.md",
"chars": 13,
"preview": "# shadowsocks"
},
{
"path": "article/shadowsocks/shadowsocks-rust/README.md",
"chars": 649,
"preview": "# shadowsocks-rust\r\n\r\nshadowsocks-rust\r\n```sh\r\nwget https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1"
},
{
"path": "article/v8/README.md",
"chars": 134,
"preview": "# 目录\n* [V8的编译实战](../v8/ninja_v8/README.md)\n* [利用v8引擎实现运行js文件](../v8/run_js/README.md)\n* [给自己的JS引擎插上HTTP的翅膀](../v8/add_ht"
},
{
"path": "article/v8/add_http/README.md",
"chars": 11522,
"preview": "# 给自己的JS引擎插上HTTP的翅膀\n这篇文章接着上篇文章《[利用v8引擎实现运行js文件](https://github.com/zy445566/myBlog/tree/master/20180708v8/20180714_run_j"
},
{
"path": "article/v8/add_http/http.cc",
"chars": 10194,
"preview": "#include <stdio.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <string.h>\n#include <ctype.h>\n#include <unis"
},
{
"path": "article/v8/add_http/http.h",
"chars": 108,
"preview": "#include <sys/socket.h>\n#include <include/v8.h>\n\nvoid Http(const v8::FunctionCallbackInfo<v8::Value>& args);"
},
{
"path": "article/v8/add_http/http.js",
"chars": 261,
"preview": "http(function(req,resp){\n\n console.log(\"req:\",JSON.stringify(req),\"\\r\\n\")\n \n resp.headers.TestHeader = \"test he"
},
{
"path": "article/v8/add_http/lastBUILD.gn",
"chars": 335,
"preview": "v8_executable(\"zy_node\") {\n sources = [\n \"zy_node_src/zy_node.cc\",\n \"zy_node_src/http.h\",\n \"zy_node_src/http.c"
},
{
"path": "article/v8/add_http/zy_node.cc",
"chars": 7856,
"preview": "#include <include/v8.h>\n#include <include/libplatform/libplatform.h>\n#include <assert.h>\n#include <fcntl.h>\n#include <st"
},
{
"path": "article/v8/ninja_v8/README.md",
"chars": 1584,
"preview": "# V8的编译实战\n感觉很少人把实战放出,都是理论为主,所以现在准备把一系列的V8实战教程放出。并附上使用V8构造到实现自己的js运行时教程,让每个人都可以参与v8的开发中。但由于工作原因,将不定时更新(预计2到3周更新一次)。\n\n# 准备"
},
{
"path": "article/v8/run_js/README.md",
"chars": 10153,
"preview": "# 利用v8引擎实现运行js文件\n这篇文章接着上篇文章《[V8的编译实战](https://github.com/zy445566/myBlog/tree/master/20180708v8/20180708_ninja_v8)》,相信大家"
},
{
"path": "article/v8/run_js/lastBUILD.gn",
"chars": 282,
"preview": "v8_executable(\"zy_node\") {\n sources = [\n \"zy_node_src/zy_node.cc\",\n \"src/d8-console.cc\",\n \"src/d8-console.h\",\n"
},
{
"path": "article/v8/run_js/zy_node.cc",
"chars": 7515,
"preview": "#include <include/v8.h>\n#include <include/libplatform/libplatform.h>\n#include <assert.h>\n#include <fcntl.h>\n#include <st"
},
{
"path": "article/wasm/README.md",
"chars": 90,
"preview": "# emscripten\n* [使用emscripten实现js直接调用C代码(20181024)](../wasm/emscripten-calling-c/README.md)"
},
{
"path": "article/wasm/emscripten-calling-c/README.md",
"chars": 2007,
"preview": "# 使用emscripten实现js直接调用C代码\n最近感觉一个时间转换的C库挺好用的,但不想做成C扩展,并不是说C扩展难,对于我来说好歹也是写过一些C扩展的,主要是C扩展对环境有一些依赖,比如非win下需要node-gyp做为环境支持来进"
},
{
"path": "article/wasm/emscripten-calling-c/add.c",
"chars": 95,
"preview": "#include <emscripten.h>\n// 实现一个加法\nEMSCRIPTEN_KEEPALIVE\nint add(int a,int b) {\n return a+b;\n}"
},
{
"path": "article/wasm/emscripten-calling-c/add.js",
"chars": 100720,
"preview": "// The Module object: Our interface to the outside world. We import\n// and export values on it. There are various ways M"
},
{
"path": "article/wasm/emscripten-calling-c/test.js",
"chars": 300,
"preview": "let addModule = require('./add.js');\n// let add = addModule.cwrap('add', 'number', ['number','number']);\naddModule.onRun"
},
{
"path": "article/windows/README.md",
"chars": 85,
"preview": "# 如何解决windows开发烦恼\n* [windows开发烦恼之win10的神器choco(20191209)](../windows/choco/README.md)"
},
{
"path": "article/windows/choco/README.md",
"chars": 804,
"preview": "# windows开发烦恼之win10的神器choco\n每次接收到windows电脑来做开发就神烦,毕竟每次要装各种软件管理特别混乱,感觉终端在windows中被极大削弱了,同时还有和服务器的环境兼容问题。但还好有WSL来解决环境兼容问题,"
}
]
// ... and 6 more files (download for full content)
About this extraction
This page contains the full source code of the zy445566/myBlog GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 205 files (9.2 MB), approximately 2.4M tokens, and a symbol index with 467 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.