这篇文章是下面这篇文章的拓展,因为我提出了一些新概念,比较容易忘记,所以要在这里记录下来。
上面这篇文章里面我没有对StackFarm内的栈帧移动、分配等进行详细定义,这里我把现在思考的一种模式记录下来。
首先是一个基础的图示:
这里是子栈帧移动导致父栈帧进行生长的一个例子。这里我们把栈帧因其他栈帧而发生变化的过程称为胁迫Coerce。
这里是一个兄弟栈帧被胁迫向右移动的案例。
很明显上面这个例子如果兄弟栈帧碰撞到父栈帧的边缘后就会像图一一样,将父栈帧胁迫变长。如下图:
可以很明显的看到,胁迫是有一个传播链条的,这里我们将胁迫传播的链条称为胁迫链CoerceChain。引入这个概念是因为下面会使用缓存将胁迫链保存起来提高性能。
我写了个StackActionPrinciple,下面贴出来,有很多语法错误,但凑合着能看:
//StackFrame Action Principle //Here we use 'Knock' to imply two frame in one channel have intersection. //We use 'Allocate' to imply the frame wanna change its position(start length) within channel. //We use 'Move' to imply the frame wanna change parent.But here its a combinition action(Delete=>(Shrink)=>Allocate=>(sibling/parent ReAllocate)); //1. Extend Parent Unless No Space for Children to Occupy //2. Move Children(Not Change Parent) //2.1 First Check EffectedSibling's Spare // Try Occupy Spare //2.3 No Spare // Try Push Sibling util Find Space Or Knock Parent Edge. //2.4 If Knocked Parent Edge,parent try step 2. //3. Move Children to Another Parent // hover on the parent you wanna move to & drop. //3.1 Modify Siblings' SpareSpace. Maybe I can Shrink it while user Halt. //3.2 Hide Original & Draw Phatom StackFrame over upper layer(helper layer?) //3.3 Drop: Just use Allocate's Logic traverse children & push them back to create clean space. //4. Delete: Simple logic. //I want to discuss the effection spread steps while a little change in branch node cause entire tree change. //1.Trigger Frame change length/position(Maybe I need some limitations on FrameChangeAction) //2.Consume Sibling SurroundingSpare(this spare space is the direction of trigger frame growth specifically) //3.Knock Sibling //4.Push Knocked Sibling to Consume its SurroundingSpare //5.Pushed All Siblings in this direction //6.The first or last Sibling Knocked Parent's Edge //7.Parent try Consume Sibling SurroundingSpare.Jump step 2 //8.Root Node Effected.
这里的规则实际就是一个胁迫链构成的顺序,从逻辑上简单推理也可以得出。这里我们就只对SurroundingSpare解释下。这里的SurroundingSpare是特指栈帧实现中两个值,leftSpare、rightSpare。他们分别代表栈帧左右两侧空闲的长度。移动或分配栈帧首先会考虑Occupy SurroundingSpare。
这里我打算将胁迫链存储起来,而存储的方法是参考React Reconciler内对Lanes的设计。但我这里实际简化很多。
这里我准备了9个buffer,每个buffer对应这一个传播链,而这个传播链在StackFrame上通过一个lanes 属性上的bit flag确定。同时需要注意的是
这里还没有改好,改好再说明最终应该有16个带版本号的胁迫链buffer。8个左方向,8个有方向。详细见下面代码实现中的bit flag定义
这里我打算使用TaskLoop的形式对这个递归操作进行实现。
胁迫链更新体系设计
上文中介绍了关于胁迫链的定义,这里把一些特殊情况考虑一下。
这里形成了从A=>B=>C=>D的胁迫链,现在架设我们将这个胁迫链存储到了胁迫链快查表中,我们如果需要向右移动(拓展)A、B、C中任意一个对象,都只需要直接更改CD的位置(长度),这样我们的胁迫链就起到了快查的作用。
A向右移动(生长)
这里的A向右移动(生长)会迫使B向右移动,而B接触到C边界所以会胁迫C进行拓展,而C拓展会迫使D向右移动。
但我们如果将A栈帧向左侧挪动一点位置,让AB不在Knock。
这样,我们的胁迫链就在AB间发生了断裂,但是我们可以看到,原A=>B=>C=>D胁迫链仍有重新连接的可能,即A再次与B进行Knock。这里我们保存这个胁迫链,进行懒更新。这里我们实际上存储的链表已经不是真正的胁迫链了,而应该叫潜在胁迫链。
这里我们定义一下懒更新,懒更新在此处为只有发生A、B操作或AB间插入新对象才进行胁迫链检查。其他时候不直接对该胁迫链操作,而是保留该胁迫链创建的时间,设计Expire时间,当胁迫链生存时间ExceedExpireTime或在已可用空胁迫链数量为0且该胁迫链最古老时,将该胁迫链清楚。
—未完待续—- 明天再写
代码实现
//bit flag: VUL(2)_VER(4)_LEFTCOERCE(8)_RIGHTCORECE(8) //VUL vulnerable export const VULNERABLE_FLAGS = /* */ 0b11_000000_00000000_00000000; export const LEFT_VULNERABLE_FLAG = /* */ 0b10_000000_00000000_00000000; export const RIGHt_VULNERABLE_FLAG = /* */ 0b01_000000_00000000_00000000; //VER version flags //MAX VERSION = 111111= export const VERSION_FLAGS = /* */ 0b00_111111_00000000_00000000; export const BUMP_VER_FLAG = /* */ 0b00_000001_00000000_00000000; //LEFT COERCE CHAIN FLAGS export const LEFT_COERCE_FLAGS = /* */ 0b00_000000_11111111_00000000; export const LEFT_COERCE_1FLAG = /* */ 0b00_000000_00000001_00000000; export const LEFT_COERCE_2FLAG = /* */ 0b00_000000_00000010_00000000; export const LEFT_COERCE_3FLAG = /* */ 0b00_000000_00000100_00000000; export const LEFT_COERCE_4FLAG = /* */ 0b00_000000_00001000_00000000; export const LEFT_COERCE_5FLAG = /* */ 0b00_000000_00010000_00000000; export const LEFT_COERCE_6FLAG = /* */ 0b00_000000_00100000_00000000; export const LEFT_COERCE_7FLAG = /* */ 0b00_000000_01000000_00000000; export const LEFT_COERCE_8FLAG = /* */ 0b00_000000_10000000_00000000; //RIGHT COERCE CHAIN FLAGS export const RIGHT_COERCE_FLAGS = /* */ 0b00_000000_00000000_11111111; export const RIGHT_COERCE_1FLAG = /* */ 0b00_000000_00000000_00000001; export const RIGHT_COERCE_2FLAG = /* */ 0b00_000000_00000000_00000010; export const RIGHT_COERCE_3FLAG = /* */ 0b00_000000_00000000_00000100; export const RIGHT_COERCE_4FLAG = /* */ 0b00_000000_00000000_00001000; export const RIGHT_COERCE_5FLAG = /* */ 0b00_000000_00000000_00010000; export const RIGHT_COERCE_6FLAG = /* */ 0b00_000000_00000000_00100000; export const RIGHT_COERCE_7FLAG = /* */ 0b00_000000_00000000_01000000; export const RIGHT_COERCE_8FLAG = /* */ 0b00_000000_00000000_10000000; //NO_IDLE_CORECE_MASK export const NO_IDLE_CORECE_MASK = /* */ 0b00_000000_11111111_11111111; export const NO_COERCE = /* */ 0b00_000000_00000000_00000000; export type CORECE_STATUS = number; export interface CoerceAbility { flag: CORECE_STATUS; } export const COERCE_NODE = 0b0000_0000_0000_0000; export interface CoerceNode<T extends CoerceAbility> { next: CoerceNode<T>; payload: T; previous: CoerceNode<T>; } export let CURRENT_COERCE = /* */ 0b00_000000_00000000_00000000; export const MAX_LEFT_COERCE = /* */ 0b00_000000_10000000_00000000; export const BASE_LEFT_COERCE = /* */ 0b00_000000_00000001_00000000; export let CURRENT_NEWEST_LEFT_COERCE = /* */ 0b00_000000_10000000_00000000; export const SET_CURRENT_NEWEST_LEFT_COERCE = (next: CORECE_STATUS) => { return (CURRENT_NEWEST_LEFT_COERCE = next); }; export const MAX_RIGHT_COERCE = /* */ 0b00_000000_00000000_10000000; export const BASE_RIGHT_COERCE = /* */ 0b00_000000_00000000_00000001; export let CURRENT_NEWEST_RIGHT_COERCE = /* */ 0b00_000000_00000000_10000000; export const SET_CURRENT_NEWEST_RIGHT_COERCE = (next: CORECE_STATUS) => { return (CURRENT_NEWEST_RIGHT_COERCE = next); }; export let PERM_COERCE = /* */ 0b00_000000_00000000_00000000; export const SET_PERM_COERCE = (next: CORECE_STATUS) => { return (PERM_COERCE = next); };
—-代码正在施工中—-
自从发现CodeSandbox后,只要能在这上面写的中小型代码我都会选择用CodeSandbox。因此这里也会使用CodeSandbox编写,这里我把这个沙盒嵌入下方(应该需要VPN访问, 但我的网站没有开国外的CDN,所以需要看的读者开绕过国内IP代理的梯子模式):
v0.1 wep 添加StackFrameActionPrinciple概念介绍 2022/03/06