Noitidart Noitidart - 4 months ago 31
Linux Question

FD_SET and FD_ISSET macros written in javascript

My friend and I worked on this a while ago. It is meant for use with js-ctypes. In Linux there are these macros for dealing with adding a list of file descriptors (uint32's) to byte array:

FD_SET
and
FD_IS_SET
. The docs are here - http://linux.die.net/man/2/select

I was wondering if anyone would be able to check if I did this right or does anyone know of anyone that has done in this in javascript? I need to complete 32bit/64bit support for big and little endian but if it's already out there I would love to see it as when we worked on this we had so many uncertainties.

Here is the code, the
fd_set_get_idx
was the helper function this is all based on.

var MACROS = {
fd_set_set: function(fdset, fd) {
let { elem8, bitpos8 } = MACROS.fd_set_get_idx(fd);
console.info('elem8:', elem8.toString());
console.info('bitpos8:', bitpos8.toString());
fdset[elem8] = 1 << bitpos8;
},
fd_set_isset: function(fdset, fd) {
let { elem8, bitpos8 } = MACROS.fd_set_get_idx(fd);
console.info('elem8:', elem8.toString());
console.info('bitpos8:', bitpos8.toString());
return !!(fdset[elem8] & (1 << bitpos8));
},
fd_set_get_idx: function(fd) {
if (osname == 'darwin' /*is_mac*/) {
// We have an array of int32. This should hopefully work on Darwin
// 32 and 64 bit.
let elem32 = Math.floor(fd / 32);
let bitpos32 = fd % 32;
let elem8 = elem32 * 8;
let bitpos8 = bitpos32;
if (bitpos8 >= 8) { // 8
bitpos8 -= 8;
elem8++;
}
if (bitpos8 >= 8) { // 16
bitpos8 -= 8;
elem8++;
}
if (bitpos8 >= 8) { // 24
bitpos8 -= 8;
elem8++;
}

return {'elem8': elem8, 'bitpos8': bitpos8};
} else { // else if (osname == 'linux' /*is_linux*/) { // removed the else if so this supports bsd and solaris now
// :todo: add 32bit support
// Unfortunately, we actually have an array of long ints, which is
// a) platform dependent and b) not handled by typed arrays. We manually
// figure out which byte we should be in. We assume a 64-bit platform
// that is little endian (aka x86_64 linux).
let elem64 = Math.floor(fd / 64);
let bitpos64 = fd % 64;
let elem8 = elem64 * 8;
let bitpos8 = bitpos64;
if (bitpos8 >= 8) { // 8
bitpos8 -= 8;
elem8++;
}
if (bitpos8 >= 8) { // 16
bitpos8 -= 8;
elem8++;
}
if (bitpos8 >= 8) { // 24
bitpos8 -= 8;
elem8++;
}
if (bitpos8 >= 8) { // 32
bitpos8 -= 8;
elem8++;
}
if (bitpos8 >= 8) { // 40
bitpos8 -= 8;
elem8++;
}
if (bitpos8 >= 8) { // 48
bitpos8 -= 8;
elem8++;
}
if (bitpos8 >= 8) { // 56
bitpos8 -= 8;
elem8++;
}

return {'elem8': elem8, 'bitpos8': bitpos8};
}
}
};

Answer

If you want to emulate the FD_XXX family of functions to the point of using a fixed size continuous area of memory you can use an Uint8Array.

Here a sample code, in analogy to the C functions I didn't use "methods":

function FDSET(setSize)
{
    var buffer = new Uint8Array(div(setSize, 8) + 1);

    function div(a, b)
    {
        return Math.floor(a / b);
    }

    this.setBit = function(index)
    {
        buffer[div(index, 8)] |= 1 << (index % 8);
    };

    this.clearBit = function(index)
    {
        buffer[div(index, 8)] &= ~(index % 8);
    };

    this.getBit = function(index)
    {
        return buffer[div(index, 8)] & 1 << (index % 8);
    };

    this.zero = function()
    {
        buffer.fill(0);
    }
}

function FD_SET(fd, fdset)
{
    fdset.setBit(fd);
}

function FD_ISSET(fd, fdset)
{
    return fdset.getBit(fd);
}

function FD_CLR(fd, fdset)
{
    return fdset.clearBit(fd);
}

function FD_ZERO(fdset)
{
    return fdset.zero();
}

It can be used just like the C counterpart

    var fdset = new FDSET(256);

    FD_SET(3, fdset);
    FD_SET(8, fdset);
    FD_CLR(3, fdset);
    console.log(FD_ISSET(8, fdset));
    FD_ZERO(fdset);
    console.log(FD_ISSET(8, fdset));

Note that, as per specifications, FD_ISSET doesn't have to return value in the range 0-1, it return a non zero value if the descriptor is present in the set.

Endianess is not an issue in this scenario as only bytes are used.
Bits ordering can be an issue if you have to pass serialized data to architectures where the MSb is on the left.
But that's usually not a concern for JS developers.


If you just want to roughly emulate the FD_XXX family, then it is even simpler:

function FD_SET(fd, fdset)
{
    fdset[fd] = 1;
}

function FD_ISSET(fd, fdset)
{
    return !!fdset[fd];
}

function FD_CLR(fd, fdset)
{
    return fdset[fd] = 0;
}

function FD_ZERO(fdset)
{
    return fdset.splice(0);
}

You can use any object, including arrays, as a fdset argument:

    var fdset = [];

    FD_SET(3, fdset);
    FD_SET(8, fdset);
    FD_CLR(3, fdset);
    console.log(FD_ISSET(8, fdset));
    FD_ZERO(fdset);
    console.log(FD_ISSET(8, fdset));