mirror of
https://github.com/Gongxh0901/kunpolibrary
synced 2025-11-05 05:45:48 +00:00
first commit
This commit is contained in:
195
src/tool/DataStruct/BinaryHeap.ts
Normal file
195
src/tool/DataStruct/BinaryHeap.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-07
|
||||
* @Description: 二叉堆(默认最小堆) 支持最大堆和最小堆
|
||||
*/
|
||||
|
||||
export abstract class HeapNode {
|
||||
public index: number;
|
||||
public abstract lessThan(other: HeapNode): boolean;
|
||||
}
|
||||
|
||||
|
||||
export class BinaryHeap<T extends HeapNode> {
|
||||
private _nodes: Array<T>;
|
||||
private _size: number;
|
||||
private _capacity: number;
|
||||
|
||||
constructor(capacity: number) {
|
||||
this._size = 0;
|
||||
this._capacity = capacity <= 0 ? 4 : capacity;
|
||||
this._nodes = new Array<T>(this._capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空
|
||||
*/
|
||||
public clear(): void {
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点
|
||||
* @param index 节点索引
|
||||
*/
|
||||
public get(index: number): T {
|
||||
return this._nodes[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取顶部节点
|
||||
*/
|
||||
public top(): T {
|
||||
return this._nodes[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含节点
|
||||
* @param node 节点
|
||||
*/
|
||||
public contains(node: T): boolean {
|
||||
return node.index >= 0 && node.index < this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push节点
|
||||
* @param node 节点
|
||||
*/
|
||||
public push(node: T): void {
|
||||
const size = ++this._size;
|
||||
|
||||
if (size > this._capacity) {
|
||||
this._capacity = this._nodes.length *= 2;
|
||||
}
|
||||
|
||||
this._sortUp(node, size - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop节点
|
||||
* @returns
|
||||
*/
|
||||
public pop(): T {
|
||||
if (this._size == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nodes = this._nodes;
|
||||
const node = nodes[0];
|
||||
|
||||
node.index = -1;
|
||||
nodes[0] = null;
|
||||
|
||||
const size = --this._size;
|
||||
|
||||
if (size > 0) {
|
||||
const finalNode = nodes[size];
|
||||
|
||||
nodes[size] = null;
|
||||
this._sortDown(finalNode, 0);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除节点
|
||||
* @param node 要移除的节点
|
||||
*/
|
||||
public remove(node: T): void {
|
||||
if (!this.contains(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size = --this._size;
|
||||
const nodes = this._nodes;
|
||||
const newNode = (nodes[node.index] = nodes[size]);
|
||||
|
||||
newNode.index = node.index;
|
||||
nodes[size] = null;
|
||||
this.update(newNode);
|
||||
node.index = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
* @param node 要更新的节点
|
||||
*/
|
||||
public update(node: T): boolean {
|
||||
if (!this.contains(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const index = node.index;
|
||||
const nodes = this._nodes;
|
||||
|
||||
if (index > 0 && nodes[index].lessThan(nodes[this._parent(index)])) {
|
||||
this._sortUp(nodes[index], index);
|
||||
} else {
|
||||
this._sortDown(nodes[index], index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private _parent(index: number): number {
|
||||
return (index - 1) >> 1;
|
||||
}
|
||||
|
||||
public get count(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
public get empty(): boolean {
|
||||
return this._size == 0;
|
||||
}
|
||||
|
||||
private _sortUp(node: T, index: number): void {
|
||||
let parentIndex = this._parent(index);
|
||||
const nodes = this._nodes;
|
||||
|
||||
while (index > 0 && node.lessThan(nodes[parentIndex])) {
|
||||
nodes[parentIndex].index = index;
|
||||
nodes[index] = nodes[parentIndex];
|
||||
index = parentIndex;
|
||||
parentIndex = this._parent(parentIndex);
|
||||
}
|
||||
|
||||
node.index = index;
|
||||
nodes[index] = node;
|
||||
}
|
||||
|
||||
private _sortDown(node: T, index: number): void {
|
||||
let childIndex = (index << 1) + 1;
|
||||
const nodes = this._nodes;
|
||||
const size = this._size;
|
||||
|
||||
while (childIndex < size) {
|
||||
let newParent = node;
|
||||
|
||||
// left
|
||||
if (nodes[childIndex].lessThan(newParent)) {
|
||||
newParent = nodes[childIndex];
|
||||
}
|
||||
|
||||
// right
|
||||
if (childIndex + 1 < size && nodes[childIndex + 1].lessThan(newParent)) {
|
||||
++childIndex;
|
||||
newParent = nodes[childIndex];
|
||||
}
|
||||
|
||||
if (node == newParent) {
|
||||
break;
|
||||
}
|
||||
|
||||
// swap down
|
||||
newParent.index = index;
|
||||
nodes[index] = newParent;
|
||||
index = childIndex;
|
||||
childIndex = (childIndex << 1) + 1;
|
||||
}
|
||||
|
||||
node.index = index;
|
||||
nodes[index] = node;
|
||||
}
|
||||
}
|
||||
301
src/tool/DataStruct/LinkedList.ts
Normal file
301
src/tool/DataStruct/LinkedList.ts
Normal file
@@ -0,0 +1,301 @@
|
||||
|
||||
function defaultEquals<T>(a: T, b: T): boolean {
|
||||
return a === b;
|
||||
}
|
||||
|
||||
/** 单链表结结构节点 */
|
||||
export class LinkedNode<T> {
|
||||
public element: T;
|
||||
public next: LinkedNode<T>; // 下一项元素的指针
|
||||
constructor(element: T) {
|
||||
this.element = element;
|
||||
this.next = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** 双向链表结结构节点 */
|
||||
export class DoublyNode<T> extends LinkedNode<T> {
|
||||
public prev: DoublyNode<T>; // 上一项元素的指针
|
||||
public next: DoublyNode<T>; // 下一元素的指针(重新定义下一个元素的类型)
|
||||
constructor(element: T) {
|
||||
super(element);
|
||||
this.prev = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** 单向链表 */
|
||||
export class LinkedList<T> {
|
||||
protected _equalsFn: (a: T, b: T) => boolean;
|
||||
protected _count: number;
|
||||
protected _head: LinkedNode<T>;
|
||||
/**
|
||||
* create
|
||||
* @param equalsFn 比较是否相等(支持自定义)
|
||||
*/
|
||||
constructor(equalsFn?: (a: T, b: T) => boolean) {
|
||||
this._equalsFn = equalsFn || defaultEquals;
|
||||
this._count = 0;
|
||||
this._head = undefined;
|
||||
}
|
||||
|
||||
/** 向链表尾部添加元素 */
|
||||
public push(element: T): void {
|
||||
const node = new LinkedNode<T>(element);
|
||||
let current: LinkedNode<T>;
|
||||
if (this._head === undefined) {
|
||||
this._head = node;
|
||||
} else {
|
||||
current = this._head;
|
||||
while (current.next !== undefined) {
|
||||
current = current.next;
|
||||
}
|
||||
current.next = node;
|
||||
}
|
||||
this._count++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 在链表的指定位置插入一个元素。
|
||||
* @param element 要插入的元素。
|
||||
* @param index 插入位置的索引,从0开始计数。
|
||||
* @returns 如果插入成功返回true,否则返回false。
|
||||
*/
|
||||
public insert(element: T, index: number): boolean {
|
||||
if (index >= 0 && index <= this._count) {
|
||||
const node = new LinkedNode<T>(element);
|
||||
if (index === 0) {
|
||||
const current = this._head;
|
||||
node.next = current;
|
||||
this._head = node;
|
||||
} else {
|
||||
const previous = this.getElementAt(index - 1);
|
||||
const current = previous.next;
|
||||
node.next = current;
|
||||
previous.next = node;
|
||||
}
|
||||
this._count++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取链表中指定位置的元素,如果不存在返回 underfined
|
||||
* @param index
|
||||
*/
|
||||
public getElementAt(index: number): LinkedNode<T> {
|
||||
if (index >= 0 && index <= this._count) {
|
||||
let node = this._head;
|
||||
for (let i = 0; i < index && node !== undefined; i++) {
|
||||
node = node.next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从链表中移除一个元素
|
||||
* @param element
|
||||
*/
|
||||
public remove(element: T): T {
|
||||
return this.removeAt(this.indexOf(element));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从链表的特定位置移除一个元素
|
||||
* @param index
|
||||
*/
|
||||
public removeAt(index: number): T {
|
||||
if (index >= 0 && index < this._count) {
|
||||
let current = this._head;
|
||||
if (index === 0) {
|
||||
this._head = current.next;
|
||||
} else {
|
||||
const previous = this.getElementAt(index - 1);
|
||||
current = previous.next;
|
||||
previous.next = current.next;
|
||||
}
|
||||
this._count--;
|
||||
current.next = undefined;
|
||||
return current.element;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回元素在链表中的索引,如果没有则返回-1
|
||||
* @param element
|
||||
*/
|
||||
public indexOf(element: T): number {
|
||||
let current = this._head;
|
||||
for (let i = 0; i < this._count && current !== undefined; i++) {
|
||||
if (this._equalsFn(element, current.element)) {
|
||||
return i;
|
||||
}
|
||||
current = current.next;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._head = undefined;
|
||||
this._count = 0;
|
||||
}
|
||||
|
||||
public getHead(): LinkedNode<T> {
|
||||
return this._head;
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
return this.size() === 0;
|
||||
}
|
||||
|
||||
public size(): number {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
if (this._head === undefined) {
|
||||
return "";
|
||||
}
|
||||
let objString = `${this._head.element}`;
|
||||
let current = this._head.next;
|
||||
for (let i = 0; i < this.size() && current !== undefined; i++) {
|
||||
objString = `${objString},${current.element}`;
|
||||
current = current.next;
|
||||
}
|
||||
return objString;
|
||||
}
|
||||
}
|
||||
|
||||
/** 双向链表 */
|
||||
export class DoublyLinkedList<T> extends LinkedList<T> {
|
||||
protected _head: DoublyNode<T>; // 重新定义 head 类型
|
||||
protected _tail: DoublyNode<T>;
|
||||
/**
|
||||
* create
|
||||
* @param equalsFn 比较是否相等(支持自定义)
|
||||
*/
|
||||
constructor(equalsFn?: (a: T, b: T) => boolean) {
|
||||
super(equalsFn);
|
||||
this._tail = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向链表尾部添加元素
|
||||
* @param element
|
||||
*/
|
||||
public push(element: T): void {
|
||||
this.insert(element, this._count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向链表指定位置添加元素
|
||||
* @param element
|
||||
* @param index
|
||||
*/
|
||||
public insert(element: T, index: number): boolean {
|
||||
if (index >= 0 && index <= this._count) {
|
||||
const node = new DoublyNode<T>(element);
|
||||
let current = this._head;
|
||||
if (index === 0) {
|
||||
if (this._head === undefined) {
|
||||
this._head = node;
|
||||
this._tail = node;
|
||||
} else {
|
||||
node.next = current;
|
||||
current.prev = node;
|
||||
this._head = node;
|
||||
}
|
||||
} else if (index === this._count) {
|
||||
current = this._tail;
|
||||
current.next = node;
|
||||
node.prev = current;
|
||||
this._tail = node;
|
||||
} else {
|
||||
const previous = this.getElementAt(index - 1);
|
||||
current = previous.next;
|
||||
node.next = current;
|
||||
previous.next = node;
|
||||
current.prev = node;
|
||||
node.prev = previous;
|
||||
}
|
||||
this._count++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从链表的特定位置移除一个元素
|
||||
* @param index
|
||||
*/
|
||||
public removeAt(index: number): T {
|
||||
if (index >= 0 && index < this._count) {
|
||||
let current = this._head;
|
||||
if (index === 0) {
|
||||
this._head = current.next;
|
||||
if (this._count === 1) {
|
||||
this._tail = undefined;
|
||||
} else {
|
||||
this._head.prev = undefined;
|
||||
}
|
||||
} else if (index === this._count - 1) {
|
||||
current = this._tail;
|
||||
this._tail = current.prev;
|
||||
this._tail.next = undefined;
|
||||
} else {
|
||||
current = this.getElementAt(index);
|
||||
const previous = current.prev;
|
||||
previous.next = current.next;
|
||||
current.next.prev = previous;
|
||||
}
|
||||
this._count--;
|
||||
current.next = undefined;
|
||||
current.prev = undefined;
|
||||
return current.element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取链表中指定位置的元素,如果不存在返回 null
|
||||
* @param index
|
||||
*/
|
||||
public getElementAt(index: number): DoublyNode<T> {
|
||||
if (index >= 0 && index <= this._count) {
|
||||
if (index > this._count * 0.5) {
|
||||
// 从后向前找
|
||||
let node = this._tail;
|
||||
for (let i = this._count - 1; i > index && node !== undefined; i--) {
|
||||
node = node.prev;
|
||||
}
|
||||
return node;
|
||||
} else {
|
||||
// 从前向后找
|
||||
let node = this._head;
|
||||
for (let i = 0; i < index && node !== undefined; i++) {
|
||||
node = node.next;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getHead(): DoublyNode<T> {
|
||||
return this._head;
|
||||
}
|
||||
|
||||
public getTail(): DoublyNode<T> {
|
||||
return this._tail;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._head = undefined;
|
||||
this._tail = undefined;
|
||||
this._count = 0;
|
||||
}
|
||||
}
|
||||
42
src/tool/DataStruct/Stack.ts
Normal file
42
src/tool/DataStruct/Stack.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { DoublyLinkedList } from "./LinkedList";
|
||||
|
||||
export class Stack<T> {
|
||||
private _items: DoublyLinkedList<T>;
|
||||
constructor(equalsFn?: (a: T, b: T) => boolean) {
|
||||
this._items = new DoublyLinkedList<T>(equalsFn);
|
||||
}
|
||||
|
||||
public push(element: T): void {
|
||||
this._items.push(element);
|
||||
}
|
||||
|
||||
public pop(): T {
|
||||
if (this.isEmpty()) {
|
||||
return undefined;
|
||||
}
|
||||
return this._items.removeAt(this.size() - 1);
|
||||
}
|
||||
|
||||
public peek(): T {
|
||||
if (this.isEmpty()) {
|
||||
return undefined;
|
||||
}
|
||||
return this._items.getTail().element;
|
||||
}
|
||||
|
||||
public size(): number {
|
||||
return this._items.size();
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
return this._items.isEmpty();
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._items.clear();
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this._items.toString();
|
||||
}
|
||||
}
|
||||
250
src/tool/MD5.ts
Normal file
250
src/tool/MD5.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
// const base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
class Crypt {
|
||||
// Bit-wise rotation left
|
||||
public static rotl(n: number, b: number): number {
|
||||
return (n << b) | (n >>> (32 - b));
|
||||
}
|
||||
|
||||
// Bit-wise rotation right
|
||||
public static rotr(n: number, b: number): number {
|
||||
return (n << (32 - b)) | (n >>> b);
|
||||
}
|
||||
|
||||
// Swap big-endian to little-endian and vice versa
|
||||
public static endianNumber(n: number): any {
|
||||
return (Crypt.rotl(n, 8) & 0x00ff00ff) | (Crypt.rotl(n, 24) & 0xff00ff00);
|
||||
}
|
||||
|
||||
// Swap big-endian to little-endian and vice versa
|
||||
public static endianArray(n: number[]): any {
|
||||
for (let i = 0, l = n.length; i < l; i++) {
|
||||
n[i] = Crypt.endianNumber(n[i]);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Generate an array of any length of random bytes
|
||||
public static randomBytes(n: number): number[] {
|
||||
const bytes = [];
|
||||
|
||||
for (; n > 0; n--) {
|
||||
bytes.push(Math.floor(Math.random() * 256));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Convert a byte array to big-endian 32-bit words
|
||||
public static bytesToWords(bytes: number[]): number[] {
|
||||
const words: any[] = [];
|
||||
|
||||
for (let i = 0, b = 0, l = bytes.length; i < l; i++, b += 8) {
|
||||
words[b >>> 5] |= bytes[i] << (24 - (b % 32));
|
||||
}
|
||||
return words;
|
||||
}
|
||||
|
||||
// Convert big-endian 32-bit words to a byte array
|
||||
public static wordsToBytes(words: number[]): number[] {
|
||||
const bytes = [];
|
||||
|
||||
for (let b = 0, l = words.length * 32; b < l; b += 8) {
|
||||
bytes.push((words[b >>> 5] >>> (24 - (b % 32))) & 0xff);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Convert a byte array to a hex string
|
||||
public static bytesToHex(bytes: number[]): string {
|
||||
const hex = [];
|
||||
|
||||
for (let i = 0, l = bytes.length; i < l; i++) {
|
||||
hex.push((bytes[i] >>> 4).toString(16));
|
||||
hex.push((bytes[i] & 0xf).toString(16));
|
||||
}
|
||||
return hex.join("");
|
||||
}
|
||||
|
||||
// Convert a hex string to a byte array
|
||||
public static hexToBytes(hex: string): number[] {
|
||||
const bytes = [];
|
||||
|
||||
for (let c = 0, l = hex.length; c < l; c += 2) {
|
||||
bytes.push(parseInt(hex.substr(c, 2), 16));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a string to a byte array
|
||||
function stringToBytes(str: string): number[] {
|
||||
str = unescape(encodeURIComponent(str));
|
||||
const bytes = [];
|
||||
|
||||
for (let i = 0, l = str.length; i < l; i++) {
|
||||
bytes.push(str.charCodeAt(i) & 0xff);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function isFastBuffer(obj: any): boolean {
|
||||
return !!obj.constructor && typeof obj.constructor.isBuffer === "function" && obj.constructor.isBuffer(obj);
|
||||
}
|
||||
|
||||
// For Node v0.10 support. Remove this eventually.
|
||||
function isSlowBuffer(obj: any): boolean {
|
||||
return typeof obj.readFloatLE === "function" && typeof obj.slice === "function" && isBuffer(obj.slice(0, 0));
|
||||
}
|
||||
|
||||
function isBuffer(obj: any): boolean {
|
||||
return obj && (isFastBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer);
|
||||
}
|
||||
|
||||
// The core
|
||||
const md5Lib = function (message: string): number[] {
|
||||
const bytes = stringToBytes(message);
|
||||
const m = Crypt.bytesToWords(bytes),
|
||||
l = bytes.length * 8;
|
||||
let ml = m.length;
|
||||
let a = 1732584193,
|
||||
b = -271733879,
|
||||
c = -1732584194,
|
||||
d = 271733878;
|
||||
|
||||
// Swap endian
|
||||
for (let i = 0; i < ml; i++) {
|
||||
m[i] = (((m[i] << 8) | (m[i] >>> 24)) & 0x00ff00ff) | (((m[i] << 24) | (m[i] >>> 8)) & 0xff00ff00);
|
||||
}
|
||||
|
||||
// Padding
|
||||
m[l >>> 5] |= 0x80 << l % 32;
|
||||
m[(((l + 64) >>> 9) << 4) + 14] = l;
|
||||
|
||||
// Method shortcuts
|
||||
const FF = md5Lib._ff,
|
||||
GG = md5Lib._gg,
|
||||
HH = md5Lib._hh,
|
||||
II = md5Lib._ii;
|
||||
|
||||
ml = m.length;
|
||||
for (let i = 0; i < ml; i += 16) {
|
||||
const aa = a,
|
||||
bb = b,
|
||||
cc = c,
|
||||
dd = d;
|
||||
|
||||
a = FF(a, b, c, d, m[i + 0], 7, -680876936);
|
||||
d = FF(d, a, b, c, m[i + 1], 12, -389564586);
|
||||
c = FF(c, d, a, b, m[i + 2], 17, 606105819);
|
||||
b = FF(b, c, d, a, m[i + 3], 22, -1044525330);
|
||||
a = FF(a, b, c, d, m[i + 4], 7, -176418897);
|
||||
d = FF(d, a, b, c, m[i + 5], 12, 1200080426);
|
||||
c = FF(c, d, a, b, m[i + 6], 17, -1473231341);
|
||||
b = FF(b, c, d, a, m[i + 7], 22, -45705983);
|
||||
a = FF(a, b, c, d, m[i + 8], 7, 1770035416);
|
||||
d = FF(d, a, b, c, m[i + 9], 12, -1958414417);
|
||||
c = FF(c, d, a, b, m[i + 10], 17, -42063);
|
||||
b = FF(b, c, d, a, m[i + 11], 22, -1990404162);
|
||||
a = FF(a, b, c, d, m[i + 12], 7, 1804603682);
|
||||
d = FF(d, a, b, c, m[i + 13], 12, -40341101);
|
||||
c = FF(c, d, a, b, m[i + 14], 17, -1502002290);
|
||||
b = FF(b, c, d, a, m[i + 15], 22, 1236535329);
|
||||
|
||||
a = GG(a, b, c, d, m[i + 1], 5, -165796510);
|
||||
d = GG(d, a, b, c, m[i + 6], 9, -1069501632);
|
||||
c = GG(c, d, a, b, m[i + 11], 14, 643717713);
|
||||
b = GG(b, c, d, a, m[i + 0], 20, -373897302);
|
||||
a = GG(a, b, c, d, m[i + 5], 5, -701558691);
|
||||
d = GG(d, a, b, c, m[i + 10], 9, 38016083);
|
||||
c = GG(c, d, a, b, m[i + 15], 14, -660478335);
|
||||
b = GG(b, c, d, a, m[i + 4], 20, -405537848);
|
||||
a = GG(a, b, c, d, m[i + 9], 5, 568446438);
|
||||
d = GG(d, a, b, c, m[i + 14], 9, -1019803690);
|
||||
c = GG(c, d, a, b, m[i + 3], 14, -187363961);
|
||||
b = GG(b, c, d, a, m[i + 8], 20, 1163531501);
|
||||
a = GG(a, b, c, d, m[i + 13], 5, -1444681467);
|
||||
d = GG(d, a, b, c, m[i + 2], 9, -51403784);
|
||||
c = GG(c, d, a, b, m[i + 7], 14, 1735328473);
|
||||
b = GG(b, c, d, a, m[i + 12], 20, -1926607734);
|
||||
|
||||
a = HH(a, b, c, d, m[i + 5], 4, -378558);
|
||||
d = HH(d, a, b, c, m[i + 8], 11, -2022574463);
|
||||
c = HH(c, d, a, b, m[i + 11], 16, 1839030562);
|
||||
b = HH(b, c, d, a, m[i + 14], 23, -35309556);
|
||||
a = HH(a, b, c, d, m[i + 1], 4, -1530992060);
|
||||
d = HH(d, a, b, c, m[i + 4], 11, 1272893353);
|
||||
c = HH(c, d, a, b, m[i + 7], 16, -155497632);
|
||||
b = HH(b, c, d, a, m[i + 10], 23, -1094730640);
|
||||
a = HH(a, b, c, d, m[i + 13], 4, 681279174);
|
||||
d = HH(d, a, b, c, m[i + 0], 11, -358537222);
|
||||
c = HH(c, d, a, b, m[i + 3], 16, -722521979);
|
||||
b = HH(b, c, d, a, m[i + 6], 23, 76029189);
|
||||
a = HH(a, b, c, d, m[i + 9], 4, -640364487);
|
||||
d = HH(d, a, b, c, m[i + 12], 11, -421815835);
|
||||
c = HH(c, d, a, b, m[i + 15], 16, 530742520);
|
||||
b = HH(b, c, d, a, m[i + 2], 23, -995338651);
|
||||
|
||||
a = II(a, b, c, d, m[i + 0], 6, -198630844);
|
||||
d = II(d, a, b, c, m[i + 7], 10, 1126891415);
|
||||
c = II(c, d, a, b, m[i + 14], 15, -1416354905);
|
||||
b = II(b, c, d, a, m[i + 5], 21, -57434055);
|
||||
a = II(a, b, c, d, m[i + 12], 6, 1700485571);
|
||||
d = II(d, a, b, c, m[i + 3], 10, -1894986606);
|
||||
c = II(c, d, a, b, m[i + 10], 15, -1051523);
|
||||
b = II(b, c, d, a, m[i + 1], 21, -2054922799);
|
||||
a = II(a, b, c, d, m[i + 8], 6, 1873313359);
|
||||
d = II(d, a, b, c, m[i + 15], 10, -30611744);
|
||||
c = II(c, d, a, b, m[i + 6], 15, -1560198380);
|
||||
b = II(b, c, d, a, m[i + 13], 21, 1309151649);
|
||||
a = II(a, b, c, d, m[i + 4], 6, -145523070);
|
||||
d = II(d, a, b, c, m[i + 11], 10, -1120210379);
|
||||
c = II(c, d, a, b, m[i + 2], 15, 718787259);
|
||||
b = II(b, c, d, a, m[i + 9], 21, -343485551);
|
||||
|
||||
a = (a + aa) >>> 0;
|
||||
b = (b + bb) >>> 0;
|
||||
c = (c + cc) >>> 0;
|
||||
d = (d + dd) >>> 0;
|
||||
}
|
||||
|
||||
return Crypt.endianArray([a, b, c, d]);
|
||||
};
|
||||
|
||||
// Auxiliary functions
|
||||
// eslint-disable-next-line max-params
|
||||
md5Lib._ff = function (a: any, b: any, c: any, d: any, x: any, s: any, t: any): any {
|
||||
const n = a + ((b & c) | (~b & d)) + (x >>> 0) + t;
|
||||
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
};
|
||||
// eslint-disable-next-line max-params
|
||||
md5Lib._gg = function (a: any, b: any, c: any, d: any, x: any, s: any, t: any): any {
|
||||
const n = a + ((b & d) | (c & ~d)) + (x >>> 0) + t;
|
||||
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
};
|
||||
// eslint-disable-next-line max-params
|
||||
md5Lib._hh = function (a: any, b: any, c: any, d: any, x: any, s: any, t: any): any {
|
||||
const n = a + (b ^ c ^ d) + (x >>> 0) + t;
|
||||
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
};
|
||||
// eslint-disable-next-line max-params
|
||||
md5Lib._ii = function (a: any, b: any, c: any, d: any, x: any, s: any, t: any): any {
|
||||
const n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
|
||||
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
};
|
||||
|
||||
/**
|
||||
* 对字符串执行md5处理
|
||||
*
|
||||
* @export
|
||||
* @param {string} message 要处理的字符串
|
||||
* @returns {string} md5
|
||||
*/
|
||||
export function md5(message: string): string {
|
||||
if (message === undefined || message === null) {
|
||||
throw new Error("Illegal argument " + message);
|
||||
}
|
||||
return Crypt.bytesToHex(Crypt.wordsToBytes(md5Lib(message)));
|
||||
}
|
||||
36
src/tool/Math.ts
Normal file
36
src/tool/Math.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
const MathMin = Math.min;
|
||||
const MathMax = Math.max;
|
||||
const MathFloor = Math.floor;
|
||||
const MathRandom = Math.random;
|
||||
|
||||
export class MathTool {
|
||||
public static clampf(value: number, min: number, max: number): number {
|
||||
return MathMin(MathMax(value, min), max);
|
||||
}
|
||||
|
||||
/** 随机 min 到 max之间的整数 (包含 min 和 max) */
|
||||
public static rand(min: number, max: number): number {
|
||||
return MathFloor(MathRandom() * (max - min + 1) + min);
|
||||
}
|
||||
|
||||
/** 随机 min 到 max之间的浮点数 (包含 min 和 max) */
|
||||
public static randRange(min: number, max: number): number {
|
||||
return MathRandom() * (max - min) + min;
|
||||
}
|
||||
|
||||
public static rad(angle: number): number {
|
||||
return (angle * Math.PI) / 180;
|
||||
}
|
||||
|
||||
public static deg(radian: number): number {
|
||||
return (radian * 180) / Math.PI;
|
||||
}
|
||||
|
||||
public static smooth(num1: number, num2: number, elapsedTime: number, responseTime: number): number {
|
||||
let out: number = num1;
|
||||
if (elapsedTime > 0) {
|
||||
out = out + (num2 - num1) * (elapsedTime / (elapsedTime + responseTime));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
7
src/tool/header.ts
Normal file
7
src/tool/header.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-14
|
||||
* @Description: tools 导出
|
||||
*/
|
||||
|
||||
|
||||
13
src/tool/helper/ObjectHelper.ts
Normal file
13
src/tool/helper/ObjectHelper.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-12
|
||||
* @Description: 对象帮助类
|
||||
*/
|
||||
export class ObjectHelper {
|
||||
public static getObjectProp(obj: Record<string, any>, key: string): any {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
return obj[key];
|
||||
}
|
||||
return (obj[key] = Object.assign({}, obj[key]));
|
||||
}
|
||||
}
|
||||
44
src/tool/log.ts
Normal file
44
src/tool/log.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-05
|
||||
* @Description: log相关的api
|
||||
*/
|
||||
|
||||
import { KUNPO_DEBUG } from "../global/header";
|
||||
|
||||
function log(...args: any[]) {
|
||||
console.log("kunpo:", ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启debug模式后 输出调试信息
|
||||
* @param args
|
||||
*/
|
||||
function debug(...args: any[]): void {
|
||||
KUNPO_DEBUG && console.log("kunpo:", ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 信息性消息 某些浏览器中会带有小图标,但颜色通常与 log 相同
|
||||
* @param args
|
||||
*/
|
||||
function info(...args: any[]): void {
|
||||
console.info("kunpo:", ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 警告信息 黄色背景,通常带有警告图标
|
||||
* @param args
|
||||
*/
|
||||
function warn(...args: any[]): void {
|
||||
console.warn("kunpo:", ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误消息 红色背景,通常带有错误图标
|
||||
* @param args
|
||||
*/
|
||||
function error(...args: any[]): void {
|
||||
console.error("kunpo:", ...args);
|
||||
}
|
||||
export { debug, error, info, log, warn };
|
||||
199
src/tool/timer/Timer.ts
Normal file
199
src/tool/timer/Timer.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-07
|
||||
* @Description: 定时器管理类
|
||||
*/
|
||||
|
||||
import { BinaryHeap } from "../DataStruct/BinaryHeap";
|
||||
import { TimerNode } from "./TimerNode";
|
||||
import { TimerNodePool } from "./TimerNodePool";
|
||||
|
||||
export class Timer {
|
||||
private _timerNodeOrder: number = 0;
|
||||
|
||||
/** 经过的时间 */
|
||||
private _elapsedTime: number = 0;
|
||||
|
||||
private _pool: TimerNodePool;
|
||||
private _heap: BinaryHeap<TimerNode>;
|
||||
|
||||
/** 暂停的计时器 */
|
||||
private _pausedTimers: Map<number, TimerNode>;
|
||||
|
||||
/**
|
||||
* 定时器数量
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
public get timerCount(): number {
|
||||
return this._heap.count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器管理类
|
||||
*
|
||||
* @param {number} initTimerCapacity 初始定时器容量
|
||||
* @memberof Timer
|
||||
*/
|
||||
public constructor(initTimerCapacity: number) {
|
||||
this._heap = new BinaryHeap<TimerNode>(initTimerCapacity);
|
||||
this._pool = new TimerNodePool(initTimerCapacity);
|
||||
this._pausedTimers = new Map<number, TimerNode>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动一个计时器
|
||||
* @param { Function } callback 回调方法
|
||||
* @param {number} interval 回调间隔 (秒)
|
||||
* @param {number} [loop=0] 重复次数:0:回调一次,1~n:回调n次,-1:无限重复
|
||||
* @returns {number} 返回计时器id
|
||||
*/
|
||||
public start(callback: () => void, interval: number, loop: number = 0): number {
|
||||
const timerNode = this._getTimerNode(callback, interval, loop);
|
||||
this._heap.push(timerNode);
|
||||
return timerNode.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定计时器
|
||||
*
|
||||
* @param {number} timerId 定时器ID
|
||||
* @memberof Timer
|
||||
*/
|
||||
public stop(timerId: number): void {
|
||||
const timerNode = this._pool.get(timerId);
|
||||
|
||||
if (timerNode) {
|
||||
if (timerNode.pause) {
|
||||
this._pausedTimers.delete(timerId);
|
||||
}
|
||||
|
||||
this._heap.remove(timerNode);
|
||||
this._pool.recycle(timerId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停定时器
|
||||
*
|
||||
* @param {number} timerId 定时器ID
|
||||
* @memberof Timer
|
||||
*/
|
||||
public pause(timerId: number): void {
|
||||
const timerNode = this._pool.get(timerId);
|
||||
|
||||
if (timerNode) {
|
||||
timerNode.pauseRemainTime = timerNode.expireTime - this._elapsedTime;
|
||||
this._heap.remove(timerNode);
|
||||
this._pausedTimers.set(timerId, timerNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续定时器
|
||||
*
|
||||
* @param {number} timerId 定时器ID
|
||||
* @memberof Timer
|
||||
*/
|
||||
public resume(timerId: number): void {
|
||||
const timerNode = this._pausedTimers.get(timerId);
|
||||
|
||||
if (timerNode) {
|
||||
timerNode.pause = false;
|
||||
timerNode.expireTime = this._elapsedTime + timerNode.pauseRemainTime;
|
||||
this._pausedTimers.delete(timerId);
|
||||
this._heap.push(timerNode);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 根据回调更新定时器
|
||||
// *
|
||||
// * @param {number} timerId 定时器ID
|
||||
// * @param {number} interval 回调间隔
|
||||
// * @param {number} loop 重复次数
|
||||
// * @param {boolean} [resetTime=false] 是否更新下次回调时间(从当前时间开始计时)
|
||||
// * @returns {boolean} 如果TimerID存在则返回true
|
||||
// * @memberof Timer
|
||||
// */
|
||||
// public updateTimer(timerId: number, interval: number, loop: number, resetTime: boolean = false): boolean {
|
||||
// const timerNode = this._pool.get(timerId);
|
||||
// if (!timerNode) {
|
||||
// return false;
|
||||
// }
|
||||
// timerNode.interval = interval;
|
||||
// timerNode.loop = loop;
|
||||
// if (resetTime) {
|
||||
// timerNode.expireTime = this._elapsedTime + interval;
|
||||
// }
|
||||
// return this._heap.update(timerNode);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 更新时钟
|
||||
*
|
||||
* @param {number} deltaTime 更新间隔
|
||||
* @memberof Timer
|
||||
*/
|
||||
public update(deltaTime: number): void {
|
||||
const elapsedTime = (this._elapsedTime += deltaTime);
|
||||
|
||||
const heap = this._heap;
|
||||
let timerNode = heap.top();
|
||||
|
||||
while (timerNode && timerNode.expireTime <= elapsedTime) {
|
||||
const callback = timerNode.callback;
|
||||
if (timerNode.loop == 0) {
|
||||
heap.pop();
|
||||
this._recycle(timerNode);
|
||||
} else if (timerNode.loop > 0) {
|
||||
// 处理多次回调定时器
|
||||
if (--timerNode.loop == 0) {
|
||||
heap.pop();
|
||||
this._recycle(timerNode);
|
||||
} else {
|
||||
// 更新下一次回调
|
||||
timerNode.expireTime = timerNode.expireTime + timerNode.interval;
|
||||
heap.update(timerNode);
|
||||
}
|
||||
} else {
|
||||
// 无限次数回调
|
||||
// 更新下一次回调
|
||||
timerNode.expireTime = timerNode.expireTime + timerNode.interval;
|
||||
heap.update(timerNode);
|
||||
}
|
||||
|
||||
callback();
|
||||
timerNode = heap.top();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有定时器
|
||||
*
|
||||
* @memberof Timer
|
||||
*/
|
||||
public clear(): void {
|
||||
this._heap.clear();
|
||||
this._pool.clear();
|
||||
this._pausedTimers.clear();
|
||||
this._timerNodeOrder = 0;
|
||||
}
|
||||
|
||||
private _getTimerNode(callback: () => void, interval: number, loop: number): TimerNode {
|
||||
const timerNode = this._pool.allocate();
|
||||
|
||||
timerNode.orderIndex = ++this._timerNodeOrder;
|
||||
timerNode.callback = callback;
|
||||
timerNode.interval = interval;
|
||||
timerNode.expireTime = this._elapsedTime + interval;
|
||||
timerNode.loop = loop;
|
||||
timerNode.pause = false;
|
||||
|
||||
return timerNode;
|
||||
}
|
||||
|
||||
private _recycle(timerNode: TimerNode): void {
|
||||
this._pool.recycle(timerNode.id);
|
||||
}
|
||||
}
|
||||
55
src/tool/timer/TimerNode.ts
Normal file
55
src/tool/timer/TimerNode.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-07
|
||||
* @Description: 计时器节点
|
||||
*/
|
||||
import { HeapNode } from "../DataStruct/BinaryHeap";
|
||||
|
||||
export class TimerNode extends HeapNode {
|
||||
/** 定时器ID */
|
||||
public id: number;
|
||||
|
||||
/** 定时器添加索引,同一时间回调根据OrderIndex排序 */
|
||||
public orderIndex: number;
|
||||
|
||||
/** 定时间隔 */
|
||||
public interval: number;
|
||||
|
||||
/** 回调时间点 */
|
||||
public expireTime: number;
|
||||
|
||||
/** 重复次数 */
|
||||
public loop: number = 0;
|
||||
|
||||
/** 定时回调 */
|
||||
public callback: () => void;
|
||||
|
||||
/** 暂停时剩余时间 */
|
||||
public pauseRemainTime: number;
|
||||
|
||||
/** 是否暂停 */
|
||||
public pause: boolean;
|
||||
|
||||
/** * 是否被回收 */
|
||||
public recycled: boolean;
|
||||
|
||||
constructor(id: number) {
|
||||
super();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否比其他定时节点小
|
||||
* @param {HeapNode} other 其他定时节点
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public lessThan(other: HeapNode): boolean {
|
||||
const otherTimerNode = other as TimerNode;
|
||||
|
||||
if (Math.abs(this.expireTime - otherTimerNode.expireTime) <= 1e-5) {
|
||||
return this.orderIndex < otherTimerNode.orderIndex;
|
||||
}
|
||||
|
||||
return this.expireTime < otherTimerNode.expireTime;
|
||||
}
|
||||
}
|
||||
123
src/tool/timer/TimerNodePool.ts
Normal file
123
src/tool/timer/TimerNodePool.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @Author: Gongxh
|
||||
* @Date: 2024-12-07
|
||||
* @Description: 计时器节点回收池
|
||||
*/
|
||||
import { TimerNode } from "./TimerNode";
|
||||
|
||||
const TimerIdBit = 19;
|
||||
const TimerCount = 1 << (32 - TimerIdBit);
|
||||
const TimerVersionMask = (1 << TimerIdBit) - 1;
|
||||
const TimerMaxVersion = TimerVersionMask;
|
||||
|
||||
export class TimerNodePool {
|
||||
private _pool: Array<TimerNode> = new Array<TimerNode>();
|
||||
private _freeIndices: Array<number> = new Array<number>();
|
||||
|
||||
/**
|
||||
* 定时器池
|
||||
* @param {number} capacity 初始容量
|
||||
*/
|
||||
public constructor(capacity: number) {
|
||||
for (let i = 0; i < capacity; ++i) {
|
||||
const timerNode = new TimerNode(i << TimerIdBit);
|
||||
|
||||
timerNode.recycled = true;
|
||||
this._pool.push(timerNode);
|
||||
this._freeIndices.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配定时器节点
|
||||
* @returns {TimerNode} 定时器节点
|
||||
*/
|
||||
public allocate(): TimerNode {
|
||||
let timerNode: TimerNode;
|
||||
const pools = this._pool;
|
||||
|
||||
if (this._freeIndices.length == 0) {
|
||||
if (pools.length == TimerCount) {
|
||||
throw new Error("超出时钟个数: " + TimerCount);
|
||||
}
|
||||
timerNode = new TimerNode(pools.length << TimerIdBit);
|
||||
pools.push(timerNode);
|
||||
} else {
|
||||
const index = this._freeIndices.pop();
|
||||
|
||||
timerNode = pools[index];
|
||||
timerNode.recycled = false;
|
||||
if ((timerNode.id & TimerVersionMask) == TimerMaxVersion) {
|
||||
throw new Error("时钟版本号过高: " + TimerMaxVersion);
|
||||
}
|
||||
++timerNode.id;
|
||||
}
|
||||
|
||||
return timerNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收定时器节点
|
||||
* @param {number} timerId 定时器ID
|
||||
*/
|
||||
public recycle(timerId: number): void {
|
||||
const index = timerId >>> TimerIdBit;
|
||||
|
||||
if (index < 0 || index >= this._pool.length) {
|
||||
throw new Error("定时器不存在");
|
||||
}
|
||||
|
||||
const timerNode = this._pool[index];
|
||||
|
||||
if (timerNode.recycled) {
|
||||
throw new Error("定时器已经被回收");
|
||||
}
|
||||
|
||||
timerNode.recycled = true;
|
||||
timerNode.callback = null;
|
||||
this._freeIndices.push(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据TimerID获取定时器节点
|
||||
* @param {number} timerId 定时器ID
|
||||
* @returns {TimerNode}
|
||||
*/
|
||||
public get(timerId: number): TimerNode | undefined {
|
||||
const index = timerId >>> TimerIdBit;
|
||||
const version = timerId & TimerVersionMask;
|
||||
|
||||
if (index < 0 || index >= this._pool.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timerNode = this._pool[index];
|
||||
if (timerNode.recycled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timerNodeVersion = timerNode.id & TimerVersionMask;
|
||||
|
||||
if (timerNodeVersion != version) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return timerNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空正在使用的Timer
|
||||
*/
|
||||
public clear(): void {
|
||||
const pools = this._pool;
|
||||
const timerNodeCount = pools.length;
|
||||
const freeIndices = this._freeIndices;
|
||||
|
||||
freeIndices.length = 0;
|
||||
for (let i = 0; i < timerNodeCount; ++i) {
|
||||
pools[i].recycled = true;
|
||||
pools[i].callback = null;
|
||||
freeIndices.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user