189 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			189 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								/*==============================================================================================================
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * 												CircQueue_t
							 | 
						|||
| 
								 | 
							
								 * 
							 | 
						|||
| 
								 | 
							
								 * =============================================================================================================
							 | 
						|||
| 
								 | 
							
								 * 描述:
							 | 
						|||
| 
								 | 
							
								 * 		这是一个环形队列, 支持入队、出队、丢弃数据等操作。
							 | 
						|||
| 
								 | 
							
								 * 		支持深拷贝入队数据,避免外部修改影响队列内部数据。
							 | 
						|||
| 
								 | 
							
								 * 		支持最大内存限制,超过限制则拒绝入队。
							 | 
						|||
| 
								 | 
							
								 * 		支持异步入队和出队操作,适用于异步场景。
							 | 
						|||
| 
								 | 
							
								 * 		支持 JSON 序列化,方便调试和存储。
							 | 
						|||
| 
								 | 
							
								 * 注意事项:
							 | 
						|||
| 
								 | 
							
								 *   	1. 队列满时入队会失败,需处理返回值
							 | 
						|||
| 
								 | 
							
								 *  	2. 出队时返回深拷贝对象,避免修改影响队列
							 | 
						|||
| 
								 | 
							
								 * 		3. 异步操作时需注意超时处理,避免死循环
							 | 
						|||
| 
								 | 
							
								 * 		4. 仅支持可序列化对象, 深拷贝基于 JSON 方法,可能无法处理函数、循环引用等复杂对象
							 | 
						|||
| 
								 | 
							
								 * 		5. 内存限制基于估算的 JSON 字符串长度
							 | 
						|||
| 
								 | 
							
								 * 示例:
							 | 
						|||
| 
								 | 
							
								 *   	const queue = new CircQueue(5, { deepCopy: true, maxMemoryBytes: 1024 }); 	// 创建容量为5的队列,启用深拷贝,最大内存限制1KB
							 | 
						|||
| 
								 | 
							
								 *   	queue.enter({ name: "Alice" });												// 入队一个对象
							 | 
						|||
| 
								 | 
							
								 *  	const item = queue.out();													// 出队一个对象,返回深拷贝							
							 | 
						|||
| 
								 | 
							
								 *  	queue.discard(2);															// 丢弃队尾2个对象
							 | 
						|||
| 
								 | 
							
								 * 		queue.send({ msg: "async item" }, 1000); 									// 异步入队,超时1秒
							 | 
						|||
| 
								 | 
							
								 * 		queue.recv(1000); 															// 异步出队,超时1秒
							 | 
						|||
| 
								 | 
							
								 * 		console.log(JSON.stringify(queue)); 										// 序列化队列内容
							 | 
						|||
| 
								 | 
							
								 *=============================================================================================================*/
							 | 
						|||
| 
								 | 
							
								class CircQueue 
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									/**
							 | 
						|||
| 
								 | 
							
									 * 构造函数
							 | 
						|||
| 
								 | 
							
									 * capacity		:队列可容纳的最大项目数
							 | 
						|||
| 
								 | 
							
									 * options		:可选的配置对象,用于自定义环形队列的行为。它支持以下配置项:
							 | 
						|||
| 
								 | 
							
									 * 					deepCopy		:控制入队时是否深拷贝元素, 默认 true
							 | 
						|||
| 
								 | 
							
									 * 					maxMemoryBytes	:队列的最大内存限制 (单位:字节),默认 Infinity, 不限制内存
							 | 
						|||
| 
								 | 
							
									 * 									  若传 1024 * 1024 代表限制 1 M ,超出则队满, 队列会拒绝入队
							 | 
						|||
| 
								 | 
							
									 */
							 | 
						|||
| 
								 | 
							
									constructor(capacity, options = {}) 
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										this.capacity = capacity;						/* 队列容量 (最多存储项目数) */
							 | 
						|||
| 
								 | 
							
										this.buffer = new Array(capacity);				/* 队列存储区,使用数组存任意对象 */
							 | 
						|||
| 
								 | 
							
										this.head = 0;									/* 队头索引 */
							 | 
						|||
| 
								 | 
							
										this.tail = 0;									/* 队尾索引 */
							 | 
						|||
| 
								 | 
							
										this.isFull = false;							/* 队满标志 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										this.deepCopy = options.deepCopy ?? true;					/* 是否启用深拷贝 (默认启用) */
							 | 
						|||
| 
								 | 
							
										this.maxMemoryBytes = options.maxMemoryBytes ?? Infinity;	/* 队列最大内存限制 (单位:字节) */
							 | 
						|||
| 
								 | 
							
										this.currentMemoryBytes = 0;								/* 当前已使用内存大小 (单位:字节) */
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* 判断队列是否为空 */
							 | 
						|||
| 
								 | 
							
									isEmpty() 
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return this.head === this.tail && !this.isFull;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* 判断队列是否已满 */
							 | 
						|||
| 
								 | 
							
									isFullFn() 
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return this.isFull;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* 估算对象大小 (以 JSON 字符串的 UTF-8 编码长度作为近似值) */
							 | 
						|||
| 
								 | 
							
									_estimateSize(obj) 
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										try 
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return Buffer.byteLength(JSON.stringify(obj), 'utf8');	/* Node.js 中用于计算字节长度 */
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										catch 
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return 0;												/* 若对象不可序列化,返回 0 (避免出错) */
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* 深拷贝对象 (基于 JSON 的方式,仅支持可序列化对象) */
							 | 
						|||
| 
								 | 
							
									_deepClone(obj)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return this.deepCopy ? JSON.parse(JSON.stringify(obj)) : obj;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* 入队操作:将对象放入队尾 */
							 | 
						|||
| 
								 | 
							
									enter(item)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										const itemSize = this._estimateSize(item);			/* 估算项目内存大小 */
							 | 
						|||
| 
								 | 
							
										if (this.isFullFn() || (this.currentMemoryBytes + itemSize > this.maxMemoryBytes))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return false;									/* 若队满或超过内存限制则入队失败 */
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										const clone = this._deepClone(item);				/* 深拷贝入队数据 (避免外部修改影响队列) */
							 | 
						|||
| 
								 | 
							
										this.buffer[this.tail] = clone;						/* 存入队尾 */
							 | 
						|||
| 
								 | 
							
										this.tail = (this.tail + 1) % this.capacity;		/* 环形增长 tail */
							 | 
						|||
| 
								 | 
							
										this.currentMemoryBytes += itemSize;				/* 增加当前内存使用 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (this.tail === this.head) this.isFull = true;	/* 若尾追头,表示队满 */
							 | 
						|||
| 
								 | 
							
										return true;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* 出队操作:从队头取出一个对象, 返回深拷贝 */
							 | 
						|||
| 
								 | 
							
									out()
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										if (this.isEmpty()) return null;						/* 队空返回 null */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										const item = this.buffer[this.head];					/* 读取队头项目 */
							 | 
						|||
| 
								 | 
							
										this.currentMemoryBytes -= this._estimateSize(item);	/* 更新内存使用 */
							 | 
						|||
| 
								 | 
							
										this.head = (this.head + 1) % this.capacity;			/* 环形增长 head */
							 | 
						|||
| 
								 | 
							
										this.isFull = false;									/* 出队必然队不满 */
							 | 
						|||
| 
								 | 
							
										return this._deepClone(item);							/* 返回深拷贝,保护队列内部数据 */
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* 丢弃数据:从队尾向前丢弃 len 项 (反向移动 tail) */
							 | 
						|||
| 
								 | 
							
									discard(len)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										for (let i = 0; i < len; i++)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											if (this.isEmpty()) return false;								/* 队空则不能继续丢弃 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											/* 定位最后一项 */
							 | 
						|||
| 
								 | 
							
											const item = this.buffer[(this.tail - 1 + this.capacity) % this.capacity];
							 | 
						|||
| 
								 | 
							
											this.currentMemoryBytes -= this._estimateSize(item);			/* 减去内存大小 */
							 | 
						|||
| 
								 | 
							
											this.tail = (this.tail - 1 + this.capacity) % this.capacity;	/* tail 回退 */
							 | 
						|||
| 
								 | 
							
											this.isFull = false;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										return true;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* 获取当前队列中元素个数 */
							 | 
						|||
| 
								 | 
							
									getDepth()
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return this.isFull
							 | 
						|||
| 
								 | 
							
											? this.capacity
							 | 
						|||
| 
								 | 
							
											: (this.tail + this.capacity - this.head) % this.capacity;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/**
							 | 
						|||
| 
								 | 
							
									 * 异步入队,支持超时退出,不会阻塞主线程, 可用于异步场景
							 | 
						|||
| 
								 | 
							
									 * buf			: 需要发送的数据
							 | 
						|||
| 
								 | 
							
									 * timeoutMs	: 超时时间
							 | 
						|||
| 
								 | 
							
									 */
							 | 
						|||
| 
								 | 
							
									async send(item, timeoutMs = 0)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										const start = Date.now();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/* 循环直到成功入队或超时 */
							 | 
						|||
| 
								 | 
							
										while (!this.enter(item))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											if (Date.now() - start >= timeoutMs) return false;		/* 超时退出 */
							 | 
						|||
| 
								 | 
							
											await new Promise(resolve => setTimeout(resolve, 1));	/* 异步轮询, 自旋加 sleep 挂起 1ms */
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										return true;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/**
							 | 
						|||
| 
								 | 
							
									 * 异步出队,支持超时退出,不会阻塞主线程, 可用于异步场景
							 | 
						|||
| 
								 | 
							
									 * buf			: 需要发送的数据
							 | 
						|||
| 
								 | 
							
									 * timeoutMs	: 超时时间
							 | 
						|||
| 
								 | 
							
									 * 返回值:成功取出一个对象(深拷贝)或 null (超时)
							 | 
						|||
| 
								 | 
							
									 */
							 | 
						|||
| 
								 | 
							
									async recv(timeoutMs = 0)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										const start = Date.now();
							 | 
						|||
| 
								 | 
							
										while (true)	
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											const item = this.out();
							 | 
						|||
| 
								 | 
							
											if (item !== null) return item;							/* 成功取出则返回 */
							 | 
						|||
| 
								 | 
							
											if (Date.now() - start >= timeoutMs) return null;		/* 超时退出 */
							 | 
						|||
| 
								 | 
							
											await new Promise(resolve => setTimeout(resolve, 1));	/* 异步轮询, 自旋加 sleep 挂起 1ms */
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/* JSON 序列化接口,用于 JSON.stringify(queue) */
							 | 
						|||
| 
								 | 
							
									toJSON()
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										const result = [];
							 | 
						|||
| 
								 | 
							
										let i = this.head;
							 | 
						|||
| 
								 | 
							
										let count = this.getDepth();
							 | 
						|||
| 
								 | 
							
										while (count-- > 0)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											result.push(this._deepClone(this.buffer[i]));			/* 复制每个元素 */
							 | 
						|||
| 
								 | 
							
											i = (i + 1) % this.capacity;							/* 移动到下一个元素 */
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										return result;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								export default CircQueue;
							 | 
						|||
| 
								 | 
							
								// module.exports = CircQueue;
							 | 
						|||
| 
								 | 
							
								
							 |