Mon 21 Jul 22:43:21 CEST 2025
This commit is contained in:
parent
4f806d1f5b
commit
c715cd3509
816
js/dos/freelist.js
Normal file
816
js/dos/freelist.js
Normal file
|
@ -0,0 +1,816 @@
|
|||
/**
|
||||
** ==================================
|
||||
** OOOO OOOO OOOO O O OOOO
|
||||
** O O O O O O O O O
|
||||
** O O O O O O O O O
|
||||
** OOOO OOOO OOOO O OOO OOOO
|
||||
** O O O O O O O O O
|
||||
** O O O O O O O O O
|
||||
** OOOO OOOO OOOO OOOO O O OOOO
|
||||
** ==================================
|
||||
** BSSLAB, Dr. Stefan Bosse http://www.bsslab.de
|
||||
**
|
||||
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
|
||||
** BY THE AUTHOR.
|
||||
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
|
||||
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
|
||||
** OUTSIDE OF THE SOFTWARE SYSTEM.
|
||||
**
|
||||
** $AUTHORS: Stefan Bosse
|
||||
** $INITIAL: (C) 2006-2016 BSSLAB
|
||||
** $CREATED: 4/28/15 by sbosse.
|
||||
** $VERSION: 1.1.3
|
||||
**
|
||||
** $INFO:
|
||||
**
|
||||
** DOS: Free cluster list management. This is a core feature
|
||||
** of the filesystem!
|
||||
**
|
||||
** Free and used cluster units:
|
||||
**
|
||||
** addr: blocks
|
||||
** size: blocks (!)
|
||||
**
|
||||
** 0. Init:
|
||||
** free_create
|
||||
**
|
||||
** 1. File Creation:
|
||||
** free_new or free_match [known final file size]
|
||||
**
|
||||
** 2. File size grow:
|
||||
** free_append [reserve enough space for further modifications
|
||||
** size > currently needed size]
|
||||
**
|
||||
** 3. File was committed:
|
||||
** free_merge [return not needed disk space]
|
||||
**
|
||||
** 4. File deletion:
|
||||
** free_merge
|
||||
**
|
||||
**
|
||||
** $ENDOFINFO
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var util = Require('util');
|
||||
var Io = Require('com/io');
|
||||
var Comp = Require('com/compat');
|
||||
var String = Comp.string;
|
||||
var Array = Comp.array;
|
||||
var Perv = Comp.pervasives;
|
||||
var div = Comp.div;
|
||||
|
||||
var Cluster_flag = {
|
||||
Cluster_FREE: 1, // The cluster is indeed free *)
|
||||
Cluster_USED: 2, // The cluster is currently used *)
|
||||
|
||||
/*
|
||||
** The left side of the free cluster follows a currently used cluster
|
||||
** with an uncommitted file. In the case, the used cluster must be
|
||||
** expanded, the beginning of this free cluster is needed. After the
|
||||
** file was committed, the RESERVED flag must be cleared to allow
|
||||
** the efficient bottom divide mode again.
|
||||
*/
|
||||
|
||||
Cluster_RESERVED: 3,
|
||||
|
||||
|
||||
print: function (flag) {
|
||||
switch (flag) {
|
||||
case Cluster_flag.Cluster_FREE: return 'Cluster_FREE';
|
||||
case Cluster_flag.Cluster_USED: return 'Cluster_USED';
|
||||
case Cluster_flag.Cluster_RESERVED: return 'Cluster_RESERVED';
|
||||
default: return 'cluster_flag?';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var Free_divide_mode = {
|
||||
Free_Bottom: 1,
|
||||
Free_Half: 2,
|
||||
Free_Top: 3
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Free list structure.
|
||||
**
|
||||
** The free cluster list is diveded in sublists with a limited
|
||||
** size range. New entries are always put on the head of the
|
||||
** list, therefore a FIFO order is achieved.
|
||||
**
|
||||
*/
|
||||
|
||||
var free_block = function (fb_addr,fb_size,fb_flag) {
|
||||
this.fb_addr= fb_addr; // int;
|
||||
this.fb_size= fb_size; // int;
|
||||
this.fb_flag= fb_flag; // cluster_flag;
|
||||
};
|
||||
|
||||
function Free_block (fb_addr,fb_size,fb_flag) {
|
||||
var obj = new free_block(fb_addr,fb_size,fb_flag);
|
||||
Object.preventExtensions(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
function fb_equal(fb1,fb2) {
|
||||
if (fb1==undefined || fb2==undefined) return false;
|
||||
else return (fb1.fb_addr==fb2.fb_addr &&
|
||||
fb1.fb_size==fb2.fb_size &&
|
||||
fb1.fb_flag==fb2.fb_flag)
|
||||
}
|
||||
|
||||
var nilfb = undefined;
|
||||
|
||||
/*
|
||||
** Free block list management. Normally, the body list is emtpy,
|
||||
** and the tail list contains the free_block list.
|
||||
*/
|
||||
|
||||
var free_block_list = function (fl_body,fl_tail,fl_biggest) {
|
||||
this.fl_body=fl_body; // free_block list; (* temporarily list *)
|
||||
this.fl_tail=fl_tail; // free_block list; (* the real list *)
|
||||
this.fl_biggest=fl_biggest; // free_block;
|
||||
};
|
||||
|
||||
function Free_block_list (fl_body,fl_tail,fl_biggest) {
|
||||
var obj = new free_block_list(fl_body,fl_tail,fl_biggest);
|
||||
Object.preventExtensions(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
free_block_list.prototype.find_biggest = function () {
|
||||
var big=undefined;
|
||||
Array.iter(this.fl_tail,function (fb) {
|
||||
if (big==undefined || fb.fb_size > big.fb_size) big=fb;
|
||||
});
|
||||
this.fl_biggest=big;
|
||||
};
|
||||
|
||||
/*
|
||||
** Divide a free cluster (addr,size') in at least a cluster
|
||||
** with newsize<=size' and if there are remains in one ore two
|
||||
** free clusters right or left and right from the middle of the
|
||||
** original cluster depending of the flags bottom.
|
||||
**
|
||||
** Mode Free_Bottom
|
||||
**
|
||||
** Before (addr,size)
|
||||
** ----------------------------------------------------
|
||||
**
|
||||
** After:
|
||||
** ++++++++XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
**
|
||||
** +: Extracted cluster
|
||||
** X: new free cluster
|
||||
**
|
||||
** Mode Free_Half
|
||||
**
|
||||
** Before (addr,size)
|
||||
** ----------------------------------------------------
|
||||
**
|
||||
** After:
|
||||
** XXXXXXXXXXXXXXXXXXXXXXXXXX++++++++YYYYYYYYYYYYYYYYYY
|
||||
**
|
||||
** +: Extracted cluster
|
||||
** X,Y: new free clusters
|
||||
**
|
||||
**
|
||||
** This scheme is used because on file creation the file server
|
||||
** not know the final size of a file. Following modify requests
|
||||
** will increase the file size, and there must be a great probability
|
||||
** for a free cluster following the current file end.
|
||||
**
|
||||
**
|
||||
** Mode Free_Top
|
||||
**
|
||||
** Before (addr,size)
|
||||
** ----------------------------------------------------
|
||||
**
|
||||
** After:
|
||||
** XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX++++++++
|
||||
**
|
||||
** +: Extracted cluster
|
||||
** X: new free cluster
|
||||
**
|
||||
** Only useable if the final size of file is known (for example
|
||||
** with a afs_CREATE request and the afs_COMMIT/SAFETY flag set).
|
||||
**
|
||||
**
|
||||
**
|
||||
** Args:
|
||||
** old: original free cluster (addr,size') :free_block
|
||||
** size: desired size from the free cluster [blocks]
|
||||
** mode:
|
||||
**
|
||||
**
|
||||
** Return:
|
||||
** new: (addr,size,flag=USED) : free_block
|
||||
** left: (addr'',size'',flag=FREE) : free_block
|
||||
** right: (addr''',size''',flag=RESERVED) : free_block
|
||||
**
|
||||
*/
|
||||
function free_divide(old,size,mode) {
|
||||
if (size < old.fb_size) {
|
||||
switch (mode) {
|
||||
case Free_divide_mode.Free_Bottom:
|
||||
return [Free_block(old.fb_addr,size,Cluster_flag.Cluster_USED),
|
||||
nilfb,
|
||||
Free_block(old.fb_addr+size,old.fb_size-size,Cluster_flag.Cluster_RESERVED)];
|
||||
break;
|
||||
case Free_divide_mode.Free_Half:
|
||||
var os2 = div(old.fb_size,2);
|
||||
if (size < os2)
|
||||
return [Free_block(old.fb_addr+os2,size,Cluster_flag.Cluster_USED),
|
||||
Free_block(old.fb_addr,os2,old.fb_flag),
|
||||
Free_block(old.fb_addr+os2+size,old.fb_size-os2-size,Cluster_flag.Cluster_RESERVED)];
|
||||
else
|
||||
return [Free_block(old.fb_addr,os2,Cluster_flag.Cluster_USED),
|
||||
nilfb,
|
||||
Free_block(old.fb_addr+os2,old.fb_size-os2,Cluster_flag.Cluster_RESERVED)];
|
||||
break;
|
||||
case Free_divide_mode.Free_Top:
|
||||
return [Free_block(old.fb_addr+old.fb_size-size,size,Cluster_flag.Cluster_USED),
|
||||
Free_block(old.fb_addr,old.fb_size-size,old.fb_flag),
|
||||
nilfb];
|
||||
break;
|
||||
}
|
||||
} else if (size == old.fb_size) {
|
||||
return [old,nilfb,nilfb]
|
||||
} else {
|
||||
return [nilfb,nilfb,nilfb]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {free_block_list []} fbs_array
|
||||
* @param {number [] []} fbs_range (start*size) array
|
||||
* @param {number} fbs_num
|
||||
*/
|
||||
var free_blocks = function (fbs_array,fbs_range,fbs_num) {
|
||||
this.fbs_array=fbs_array; // free_block_list array;
|
||||
this.fbs_range=fbs_range; // (int * int) array;
|
||||
this.fbs_num=fbs_num; // number of lists
|
||||
this.fbs_fragments=0; // current number of free clusters
|
||||
this.fbs_compacthres=50; // initial #fragments threshold for compaction run
|
||||
};
|
||||
|
||||
free_blocks.prototype.print = function () {
|
||||
var str='';
|
||||
var flar = this.fbs_array;
|
||||
//var srar = this.fbs_range;
|
||||
//var nmax = this.fbs_num - 1;
|
||||
str='Free cluster list:\n';
|
||||
Array.iter(flar,function (fcl,i) {
|
||||
str=str+' #'+i+' tail\n';
|
||||
Array.iter (fcl.fl_tail,function (fb) {
|
||||
str=str+' [addr='+fb.fb_addr+' size='+fb.fb_size+' flag='+Cluster_flag.print(fb.fb_flag)+']\n';
|
||||
});
|
||||
str=str+' #'+i+' body\n';
|
||||
Array.iter (fcl.fl_body,function (fb) {
|
||||
str=str+' [addr='+fb.fb_addr+' size='+fb.fb_size+' flag='+Cluster_flag.print(fb.fb_flag)+']\n';
|
||||
});
|
||||
});
|
||||
return str;
|
||||
};
|
||||
|
||||
/*
|
||||
** Find a free cluster (X) with start address 'addr' and remove it
|
||||
** from the free list. It's assumed that this cluster
|
||||
** is somewhere in the front of a free list. Therefore, all
|
||||
** free cluster lists are searched in a round-robinson style.
|
||||
**
|
||||
** L : AAAAAA XXXX BBBB ->
|
||||
** L': AAAAAA BBBB ->
|
||||
**
|
||||
** XXXX
|
||||
**
|
||||
*/
|
||||
free_blocks.prototype.free_find = function (addr) {
|
||||
var flar = this.fbs_array;
|
||||
var nmax = this.fbs_num;
|
||||
var found = nilfb;
|
||||
var empty = 0;
|
||||
var mask = ((1 << nmax) - 1);
|
||||
|
||||
function iter (n) {
|
||||
Array.match(flar[n].fl_tail,
|
||||
function (hd,tl) {
|
||||
if (hd.fb_addr != addr) {
|
||||
flar[n].fl_body.push(hd);
|
||||
flar[n].fl_tail = tl;
|
||||
var next = (n + 1) == nmax ? 0 : (n + 1);
|
||||
|
||||
iter(next);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
** Success.
|
||||
*/
|
||||
found = hd;
|
||||
flar[n].fl_tail=Array.merge(flar[n].fl_body, tl);
|
||||
flar[n].fl_body = [];
|
||||
|
||||
if (fb_equal(found, flar[n].fl_biggest))
|
||||
flar[n].find_biggest();
|
||||
}
|
||||
},
|
||||
function () {
|
||||
empty=empty | (1 << n);
|
||||
var next = (n+1)==nmax?0 : (n+1);
|
||||
if ((empty & mask) != mask) iter (next);
|
||||
})
|
||||
}
|
||||
iter(0);
|
||||
this.fbs_fragments=0;
|
||||
for (var i = 0; i<nmax;i++) {
|
||||
flar[i].fl_tail=Array.merge(flar[i].fl_body,flar[i].fl_tail);
|
||||
flar[i].fl_body=[];
|
||||
this.fbs_fragments=this.fbs_fragments+flar[i].fl_tail.length;
|
||||
}
|
||||
return found;
|
||||
};
|
||||
|
||||
/**
|
||||
** Insert a free cluster (X) in the appropriate free cluster list.
|
||||
** This cluster is inserted on the head of the appropriate list
|
||||
** (FIFO order).
|
||||
**
|
||||
** L: AAAAAA BBBBBB
|
||||
**
|
||||
** XXXXXX ->
|
||||
**
|
||||
** L': XXXXXX AAAAAA BBBBBB
|
||||
**
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param {free_block} fb
|
||||
*/
|
||||
free_blocks.prototype.free_insert = function (fb) {
|
||||
var flar = this.fbs_array;
|
||||
var srar = this.fbs_range;
|
||||
var nmax = this.fbs_num-1;
|
||||
var size = fb.fb_size;
|
||||
|
||||
|
||||
function iter(n) {
|
||||
var low,high;
|
||||
low=srar[n][0];
|
||||
high=srar[n][1];
|
||||
if (size >= low && size < high) {
|
||||
flar[n].fl_tail = Array.merge([fb], flar[n].fl_tail);
|
||||
if ((flar[n].fl_biggest && (fb.fb_size > flar[n].fl_biggest.fb_size)) || flar[n].fl_biggest==nilfb)
|
||||
flar[n].fl_biggest = fb;
|
||||
} else {
|
||||
if (n > 0)
|
||||
iter(n - 1);
|
||||
else
|
||||
Io.err('[FREEL] free_insert: inconsistent free list/size=' + size);
|
||||
}
|
||||
}
|
||||
if (fb!=nilfb) iter(nmax);
|
||||
};
|
||||
|
||||
/**
|
||||
** Try to merge a free cluster (X) (previously allocated
|
||||
** with free_new or free_append) after a file creation
|
||||
** with an already existing one from the free list.
|
||||
** The new cluster is flagged with Cluster_FREE! The merged cluster
|
||||
** (if any) or the cluster X is inserted in the free list again.
|
||||
**
|
||||
** L: AAAA XXXXX BBBBB ->
|
||||
**
|
||||
** XXXXXX +
|
||||
** YYYYYYYYYY =
|
||||
** ZZZZZZZZZZZZZZZZ ->
|
||||
**
|
||||
** L': AAAA ZZZZZZZZZZZZZZZZ BBBBB
|
||||
*
|
||||
* @param {free_block} fb
|
||||
*/
|
||||
free_blocks.prototype.free_merge = function (fb) {
|
||||
if (fb != nilfb) {
|
||||
fb.fb_flag = Cluster_flag.Cluster_FREE;
|
||||
var addr = fb.fb_addr + fb.fb_size;
|
||||
var size = fb.fb_size;
|
||||
|
||||
var fb2 = this.free_find(addr);
|
||||
if (fb2!=nilfb)
|
||||
this.free_insert(Free_block(fb.fb_addr,size + fb2.fb_size,fb.fb_flag));
|
||||
else
|
||||
this.free_insert(fb);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
** Search the free cluster list for a cluster with
|
||||
** start address addr. Take the front of the cluster
|
||||
** with the desired size, and insert the rest in the list again.
|
||||
** Return the extracted cluster OR undefined.
|
||||
**
|
||||
** Units:
|
||||
** addr,size : blocks
|
||||
**
|
||||
** L: AAAA XXXX BBBBB ->
|
||||
**
|
||||
** L': AAAA XX BBBBB ->
|
||||
**
|
||||
** XX
|
||||
**
|
||||
**
|
||||
*
|
||||
*
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
* @returns {free_block|undefined}
|
||||
*/
|
||||
free_blocks.prototype.free_append = function (addr,size) {
|
||||
var fb = this.free_find(addr);
|
||||
if (fb.fb_size >= size) {
|
||||
var newc, right;
|
||||
var blocks = free_divide(fb, size, Free_divide_mode.Free_Bottom);
|
||||
newc = blocks[0];
|
||||
//left = blocks[1];
|
||||
right = blocks[2];
|
||||
this.free_insert(right);
|
||||
return newc;
|
||||
}
|
||||
else {
|
||||
this.free_insert(fb);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
** Get a free cluster with specified size somewhere in the file system.
|
||||
** A file was created. The biggest cluster in a freelist is
|
||||
** taken and split to the desired size and the rest. Searched is performed
|
||||
** from the largest free clusters down to the smallest possible.
|
||||
**
|
||||
** Units:
|
||||
** size: blocks
|
||||
**
|
||||
*
|
||||
*
|
||||
* @param {number} size
|
||||
* @returns {free_block|undefined}
|
||||
*/
|
||||
free_blocks.prototype.free_new = function (size) {
|
||||
var self=this;
|
||||
|
||||
if (this.fbs_fragments>this.fbs_compacthres) self.free_compact();
|
||||
|
||||
var flar = this.fbs_array;
|
||||
var srar = this.fbs_range;
|
||||
var nmax = this.fbs_num-1;
|
||||
|
||||
|
||||
function iter (n) {
|
||||
var low;
|
||||
var found=undefined;
|
||||
function find () {
|
||||
if (flar[n].fl_biggest.fb_size >= size) {
|
||||
Array.match(flar[n].fl_tail,
|
||||
function (hd,tl){
|
||||
if (fb_equal(hd,flar[n].fl_biggest))
|
||||
{
|
||||
flar[n].fl_tail = Array.merge(flar[n].fl_body, tl);
|
||||
flar[n].fl_body = [];
|
||||
flar[n].find_biggest();
|
||||
found = hd;
|
||||
}
|
||||
else {
|
||||
Array.append(flar[n].fl_body,hd);
|
||||
flar[n].fl_tail = tl;
|
||||
find();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
low=srar[n][0];
|
||||
//high=srar[n][1];
|
||||
if (!Array.empty(flar[n].fl_tail)) {
|
||||
/*
|
||||
** Find the biggest cluster in this list!
|
||||
*/
|
||||
find();
|
||||
if (found!=undefined) {
|
||||
return found;
|
||||
}
|
||||
else {
|
||||
if (n > 0 && size < low)
|
||||
return iter(n-1);
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
} else if (size < low) {
|
||||
if (n > 0)
|
||||
return iter(n-1);
|
||||
else
|
||||
return undefined;
|
||||
|
||||
} else return undefined;
|
||||
}
|
||||
var cl = iter(nmax);
|
||||
/*
|
||||
** Move the body lists to the front of the tail lists.
|
||||
*/
|
||||
for (var i = 0; i <nmax;i++) {
|
||||
flar[i].fl_tail=Array.merge(flar[i].fl_body,flar[i].fl_tail);
|
||||
flar[i].fl_body=[];
|
||||
}
|
||||
var mode = (cl.fb_flag == Cluster_flag.Cluster_RESERVED)? Free_divide_mode.Free_Half:Free_divide_mode.Free_Bottom;
|
||||
var newc,left,right;
|
||||
var blocks = free_divide(cl,size,mode);
|
||||
newc = blocks[0];
|
||||
left = blocks[1];
|
||||
right = blocks[2];
|
||||
this.free_insert(right);
|
||||
if (mode==Free_divide_mode.Free_Half)
|
||||
self.free_insert(left);
|
||||
return newc;
|
||||
};
|
||||
|
||||
/**
|
||||
** Get a free cluster with specified size somewhere in the file system.
|
||||
** A file was created. The best matching cluster in a freelist is
|
||||
** taken and split to the desired size and the rest. The free cluster
|
||||
** is taken from the best matching free cluster list.
|
||||
** Only useful in the case the final size of a file is already
|
||||
** known (e.g. small files transferred with a single request).
|
||||
**
|
||||
** Units:
|
||||
** size: blocks
|
||||
**
|
||||
* @param {number} size
|
||||
* @returns {free_block|undefined}
|
||||
*/
|
||||
|
||||
|
||||
free_blocks.prototype.free_match = function (size) {
|
||||
var self=this;
|
||||
|
||||
if (this.fbs_fragments>this.fbs_compacthres) self.free_compact();
|
||||
|
||||
var flar = this.fbs_array;
|
||||
var srar = this.fbs_range;
|
||||
var nmax = this.fbs_num-1;
|
||||
|
||||
function iter (n) {
|
||||
var high;
|
||||
//low=srar[n][0];
|
||||
high=srar[n][1];
|
||||
if (size <= high && !Array.empty(flar[n].fl_tail)) {
|
||||
/*
|
||||
** Find the best matching cluster!
|
||||
*/
|
||||
|
||||
var found = undefined;
|
||||
Array.iter (flar[n].fl_tail, function(fb) {
|
||||
if (fb.fb_size >= size &&
|
||||
((found && found.fb_size > fb.fb_size) ||
|
||||
found==undefined))
|
||||
found = fb;
|
||||
});
|
||||
if (found==undefined) {
|
||||
if (n < nmax)
|
||||
return iter(n + 1);
|
||||
else
|
||||
return undefined;
|
||||
} else {
|
||||
|
||||
/*
|
||||
** Remove the found free cluster from the free list.
|
||||
*/
|
||||
flar[n].fl_body = [];
|
||||
Array.iter(flar[n].fl_tail, function (fb) {
|
||||
if (!fb_equal(fb, found))
|
||||
Array.append(flar[n].fl_body,fb);
|
||||
});
|
||||
flar[n].fl_tail = flar[n].fl_body;
|
||||
flar[n].fl_body = [];
|
||||
if (fb_equal(found, flar[n].fl_biggest))
|
||||
flar[n].find_biggest();
|
||||
return found;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (n < nmax)
|
||||
return iter (n+1);
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
** Start searching in the free cluster list with the smallest
|
||||
** cluster sizes!
|
||||
*/
|
||||
var cl = iter(0);
|
||||
if (cl!=undefined) {
|
||||
var newc,left;
|
||||
var blocks = free_divide(cl,size,Free_divide_mode.Free_Top);
|
||||
newc = blocks[0];
|
||||
left = blocks[1];
|
||||
if (left!=nilfb) this.free_insert(left);
|
||||
return newc;
|
||||
} else return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
** Release a reserved cluster with the start address 'addr'.
|
||||
** It' assumed that this cluster is somewhere in the front of a free list.
|
||||
** Therefore, all free cluster lists are searched in a round-robinson style.
|
||||
** After file creation is finished, normally the free_merge
|
||||
** function do this job in a more efficient way.
|
||||
**
|
||||
** L: AAAA XXXXXX(RES) BBB ->
|
||||
** L': AAAA XXXXXX(FREE) BBB
|
||||
**
|
||||
*
|
||||
* @param {number} addr
|
||||
*/
|
||||
free_blocks.prototype.free_release = function (addr) {
|
||||
var flar = this.fbs_array;
|
||||
var nmax = this.fbs_num;
|
||||
var empty = 0;
|
||||
var mask = ((1 << nmax) - 1);
|
||||
function iter (n) {
|
||||
Array.match(flar[n].fl_tail,
|
||||
function (hd,tl){
|
||||
if (hd.fb_addr != addr) {
|
||||
|
||||
Array.append(flar[n].fl_body, hd);
|
||||
flar[n].fl_tail = tl;
|
||||
var next = ((n + 1) == nmax) ? 0 : (n + 1);
|
||||
|
||||
iter(next);
|
||||
} else {
|
||||
/*
|
||||
** Success.
|
||||
*/
|
||||
hd.fb_flag = Free_block.Cluster_FREE;
|
||||
}
|
||||
},
|
||||
function () {
|
||||
empty = empty | (1 << n);
|
||||
var next = ((n+1) == nmax)?0:(n+1);
|
||||
if ((empty & mask) != mask)
|
||||
iter(next);
|
||||
})
|
||||
}
|
||||
iter(0);
|
||||
/*
|
||||
** Move the body list to the front of the tail list.
|
||||
*/
|
||||
this.fbs_fragments=0;
|
||||
for (var i = 0; i<nmax;i++) {
|
||||
flar[i].fl_tail=Array.merge(flar[i].fl_body,flar[i].fl_tail);
|
||||
flar[i].fl_body=[];
|
||||
this.fbs_fragments=this.fbs_fragments+flar[i].fl_tail.length;
|
||||
}
|
||||
|
||||
if (this.fbs_fragments>this.fbs_compacthres) self.free_compact();
|
||||
};
|
||||
|
||||
/**
|
||||
** Create a free cluster list object handler. The size range is divided
|
||||
** linear in n equidistant ranges (decades of tenth).
|
||||
*
|
||||
* @param {number} n
|
||||
* @param {number} size
|
||||
*/
|
||||
free_blocks.prototype.free_create = function (n,size) {
|
||||
var i;
|
||||
var srar = [];
|
||||
var flar = [];
|
||||
this.fbs_fragments=0;
|
||||
for (i = 0; i <n; i ++) {
|
||||
flar.push(Free_block_list([], [], nilfb));
|
||||
}
|
||||
|
||||
var d = 1;
|
||||
for (i = 0; i <n-1; i ++) {
|
||||
srar.push([d, d * 10]);
|
||||
d = d * 10;
|
||||
}
|
||||
|
||||
if (size > d*10)
|
||||
srar.push([d,size]);
|
||||
else
|
||||
srar.push([d,d*10]);
|
||||
this.fbs_array=flar;
|
||||
this.fbs_range=srar;
|
||||
this.fbs_num=n;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
** Compact the freelist (merge contiguous clusters). Simply said,
|
||||
** but hard to perform.
|
||||
*
|
||||
*
|
||||
*/
|
||||
free_blocks.prototype.free_compact = function () {
|
||||
var self=this;
|
||||
var i;
|
||||
/*
|
||||
** First merge all sub lists to one huge list.
|
||||
*/
|
||||
|
||||
var fl = [];
|
||||
for (i = 0; i<self.fbs_num; i++) {
|
||||
fl=Array.merge(fl, this.fbs_array[i].fl_tail);
|
||||
}
|
||||
/*
|
||||
** Now resort the freelist with increasing disk address
|
||||
** order.
|
||||
*/
|
||||
var fl2 = Array.sort(fl,function (f1,f2) {return (f1.fb_addr > f2.fb_addr)?1:-1});
|
||||
|
||||
/*
|
||||
** Try to merge contiguous clusters.
|
||||
*/
|
||||
var merged=[];
|
||||
var lastfb = Free_block(0,0,Cluster_flag.Cluster_FREE);
|
||||
Array.iter(fl2,function (fs) {
|
||||
if ((lastfb.fb_addr + lastfb.fb_size) != fs.fb_addr) {
|
||||
|
||||
if ((lastfb.fb_addr + lastfb.fb_size) != 0) {
|
||||
|
||||
merged.push(lastfb);
|
||||
}
|
||||
lastfb = fs;
|
||||
} else {
|
||||
|
||||
lastfb = Free_block(lastfb.fb_addr, fs.fb_size + lastfb.fb_size, lastfb.fb_flag);
|
||||
}
|
||||
});
|
||||
merged.push(lastfb);
|
||||
/*
|
||||
** Now rebuild the freelist.
|
||||
*/
|
||||
var flar = [];
|
||||
for(i=0;i<this.fbs_num;i++) {
|
||||
flar.push(Free_block_list([],[],nilfb));
|
||||
}
|
||||
this.fbs_array=flar;
|
||||
Array.iter (merged,function (fb) {
|
||||
self.free_insert(fb);
|
||||
});
|
||||
/*
|
||||
** Huhhh, all work is done.
|
||||
*/
|
||||
this.fbs_fragments=0;
|
||||
for (i = 0; i<this.fbs_num;i++) {
|
||||
this.fbs_fragments=this.fbs_fragments+flar[i].fl_tail.length;
|
||||
}
|
||||
if (this.fbs_fragments>this.fbs_compacthres) this.fbs_compacthres=this.fbs_fragments+10;
|
||||
else if (this.fbs_fragments<(this.fbs_compacthres/2)) this.fbs_compacthres=Perv.max(5,this.fbs_fragments*2);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
** Return the number of free clusters, the total free space and
|
||||
** an array containing (low,high,num) free list statistics.
|
||||
*
|
||||
* @returns {[number,number,array]} totn,tots,tota
|
||||
*/
|
||||
free_blocks.prototype.free_info = function () {
|
||||
var flar = this.fbs_array;
|
||||
var srar = this.fbs_range;
|
||||
var nmax = this.fbs_num;
|
||||
var totn = 0;
|
||||
var tots = 0;
|
||||
var tota = [];
|
||||
for (var i = 0; i < nmax; i++) {
|
||||
totn = totn + Array.length(flar[i].fl_tail);
|
||||
var count = 0;
|
||||
Array.iter(flar[i].fl_tail, function (fb) {
|
||||
tots = tots + fb.fb_size;
|
||||
count++;
|
||||
});
|
||||
var low, high;
|
||||
low = srar[i][0];
|
||||
high = srar[i][1];
|
||||
tota.push([low, high, count]);
|
||||
}
|
||||
return [totn,tots,tota]
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Cluster_flag:Cluster_flag,
|
||||
Free_block:Free_block,
|
||||
Free_blocks: function (fbs_array,fbs_range,fbs_num) {
|
||||
var obj = new free_blocks(fbs_array,fbs_range,fbs_num);
|
||||
Object.preventExtensions(obj);
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user