Skip to content

Commit

Permalink
Improve ref-type.ts
Browse files Browse the repository at this point in the history
Signed-off-by: Yonggang Luo <luoyonggang@gmail.com>
  • Loading branch information
lygstate committed Aug 4, 2021
1 parent 4ea590e commit a4da340
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 113 deletions.
5 changes: 4 additions & 1 deletion lib/ffi.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ Object.keys(bindings.FFI_TYPES).forEach(name => {
ref.types[name].ffi_type = type;
});

// make `size_t` use the "ffi_type_pointer"
// make `ssize_t` `size_t` `intptr_t` `uintptr_t` use the "ffi_type_pointer"
ref.types.ssize_t.ffi_type = bindings.FFI_TYPES.pointer;
ref.types.size_t.ffi_type = bindings.FFI_TYPES.pointer;
ref.types.intptr_t.ffi_type = bindings.FFI_TYPES.pointer;
ref.types.uintptr_t.ffi_type = bindings.FFI_TYPES.pointer;

// make `CString` use "ffi_type_pointer"
ref.types.CString.ffi_type = bindings.FFI_TYPES.pointer;
Expand Down
299 changes: 202 additions & 97 deletions lib/ref/ref.js
Original file line number Diff line number Diff line change
Expand Up @@ -664,158 +664,241 @@ exports.reinterpretUntilZeros = function reinterpretUntilZeros (buffer, size, of
return rtn;
};

function getAlignmentAndSize(nativeRef, name) {
if (name === 'ssize_t' || name === 'intptr_t' || name === 'uintptr_t') {
name = 'size_t'
}
return {
size: nativeRef.sizeof[name],
alignment: nativeRef.alignof[name],
}
}

/**
* @param {String} name The c type name
*/
function getFunctionsGenerate(max_size_is_8, unsigned, size)
{
const readFunctionNumber = unsigned ? buffer[`readUInt${size * 8}`] : buffer[`readInt${size * 8}`]
const readFunctionBigInt = unsigned ? buffer[`readBigUInt${size * 8}`] : buffer[`readBigInt${size * 8}`]

function createCType(name) {
const unsigned = name === 'bool'
|| name === 'byte'
|| name === 'size_t'
|| name[0] === 'u';
const size = nativeRef.sizeof[name];
const alignment = nativeRef.alignof[name];
assert(alignment > 0);
/* Always get as number */
let getFunctionNumber = readFunctionNumber
if (!getFunctionNumber) {
getFunctionNumber = function (buf, offset) {
return Number(readFunctionBigInt(buf, offset))
}
}

const possible_8_bytes = name === 'long'
|| name === 'ulong'
|| name === 'longlong'
|| name === 'ulonglong'
|| name === 'size_t'
|| size === 8
/* Always get as bigint */
let getFunctionBigInt = readFunctionBigInt
if (!getFunctionBigInt) {
getFunctionBigInt = function (buf, offset) {
return BigInt(readFunctionNumber(buf, offset))
}
}

/**
* getFunction
* should always return `bigint` for max_size_is_8 type,
* such as size_t, intptr_t, long
* should always return `number` for types that the type size are within 4 byte
* such as bool char uchar byte short int uint32_t
*/
let getFunction
let setFunction
if (possible_8_bytes) {
if (max_size_is_8) {
getFunction = getFunctionBigInt
} else {
getFunction = getFunctionNumber
}
return {
get: getFunction,
getNumber: getFunctionNumber,
getBigInt: getFunctionBigInt,
}
}

function setFunctionsGenerate(max_size_is_8, unsigned, size)
{
const writeFunctionNumber = unsigned ? buffer[`writeUInt${size * 8}`] : buffer[`writeInt${size * 8}`]
const writeFunctionBigInt = unsigned ? buffer[`writeBigUInt${size * 8}`] : buffer[`writeBigInt${size * 8}`]

/* Always set as number */
let setFunctionNumber = writeFunctionNumber
if (!setFunctionNumber) {
/* Means the sizeof type is 8 byte */
setFunctionNumber = function (buf, val, offset) {
return writeFunctionBigInt(buf, BigInt(val), offset)
}
}

/* Always set as bigint */
let setFunctionBigInt = writeFunctionBigInt
if (!setFunctionBigInt) {
/* Means the sizeof type is 4byte or less */
if (unsigned) {
if (size === 4) {
getFunction = function (buf, offset) {
return BigInt(buffer.readUInt32(buf, offset >>> 0));
}
setFunction = function (buf, val, offset) {
const valNum = Number(BigInt.asUintN(32, val));
return buffer.writeUInt32(buf, valNum, offset >>> 0);
}
} else {
getFunction = function (buf, offset) {
return buffer.readBigUInt64(buf, offset >>> 0);
}
setFunction = function (buf, val, offset) {
return buffer.writeBigUInt64(buf, val, offset >>> 0);
}
setFunctionBigInt = function (buf, val, offset) {
return writeFunctionNumber(buf, BitInt.asUintN(val, 32), offset)
}
} else {
if (size === 4) {
getFunction = function (buf, offset) {
return BigInt(buffer.readInt32(buf, offset >>> 0));
}
setFunction = function (buf, val, offset) {
const valNum = Number(BigInt.asIntN(32, val));
return buffer.writeInt32(buf, valNum, offset >>> 0);
}
} else {
getFunction = function (buf, offset) {
return buffer.readBigInt64(buf, offset >>> 0);
}
setFunction = function (buf, val, offset) {
return buffer.writeBigInt64(buf, val, offset >>> 0);
}
setFunctionBigInt = function (buf, val, offset) {
return writeFunctionNumber(buf, BitInt.asIntN(val, 32), offset)
}
}
}

/**
* setFunction
* should accept `bigint` only for max_size_is_8 type,
* such as size_t, intptr_t, long, uint64_t
* should accept `number` only for types that the type size are within 4 byte
* such as bool char uchar byte short int uint32_t
*/
let setFunction
if (max_size_is_8) {
setFunction = setFunctionBigInt
} else {
const readFunction = unsigned ? buffer[`readUInt${size * 8}`] : buffer[`readInt${size * 8}`]
getFunction = function (buf, offset) {
return readFunction(buf, offset >>> 0)
}
if (size === 1) {
const writeFunction = unsigned ? buffer.writeUInt8 : buffer.writeInt8
setFunction = function (buf, val, offset) {
if (typeof val === 'string') {
val = val.charCodeAt(0);
}
return writeFunction(buf, val, offset >>> 0);
}
} else {
const writeFunction = unsigned ? buffer[`writeUInt${size * 8}`] : buffer[`writeInt${size * 8}`]
setFunction = function (buf, val, offset) {
return writeFunction(buf, val, offset >>> 0)
setFunction = setFunctionNumber
}
return {
set: setFunction,
setNumeric: function (buf, val, offset) {
if (typeof val === 'bigint') {
return setFunctionBigInt(buf, val, offset)
} else {
return setFunctionNumber(buf, val, offset)
}
}
},
setNumber: setFunctionNumber,
setBigInt: setFunctionBigInt,
}
}

function overrideGetSetFunctions(name, unsigned, getFunctions, setFunctions) {
switch (name) {
case 'bool': {
let getFunctionOld = getFunction
getFunction = function (buf, offset) {
return getFunctionOld(buf, offset) ? true : false;
let getFunctionOld = getFunctions.get
getFunctions.get = function (buf, offset) {
return getFunctionOld(buf, offset) !== 0;
}
let setFunctionOld = setFunction
setFunction = function (buf, val, offset) {
if (typeof val !== 'number') {
val = val ? 1 : 0;
}
return setFunctionOld(buf, val, offset);
let setFunctionOld = setFunctions.set
setFunctions.set = function (buf, val, offset) {
/* Turn boolean to number from https://stackoverflow.com/a/22239859 */
return setFunctionOld(buf, val | 0, offset);
}
break;
}
case 'float': {
getFunction = function (buf, offset) {
return buffer.readFloat(buf, offset >>> 0);
}
setFunction = function (buf, val, offset) {
return buffer.writeFloat(buf, val, offset >>> 0);
case 'uchar':
case 'char': {
const writeFunction = unsigned ? buffer.writeUInt8 : buffer.writeInt8
setFunctions.set = function (buf, val, offset) {
return writeFunction(buf, val.charCodeAt(0), offset);
}
break;
}
case 'float': {
getFunctions.get = buffer.readFloat
setFunctions.set = buffer.writeFloat
break;
}
case 'double': {
getFunction = function (buf, offset) {
return buffer.readDouble(buf, offset >>> 0);
}
setFunction = function (buf, val, offset) {
return buffer.writeDouble(buf, val, offset >>> 0);
}
getFunctions.get = buffer.readDouble
setFunctions.set = buffer.writeDouble
break;
}
case 'Object': {
getFunction = function (buf, offset) {
getFunctions.get = function (buf, offset) {
return nativeRef.readObject(buf, offset >>> 0);
}
setFunction = function (buf, val, offset) {
getFunctions.set = function (buf, val, offset) {
nativeRef.writeObject(buf, val, offset >>> 0);
return offset + size;
}
break;
}
}
}

/**
* @param {String} name The c type name
*/

function createCType(name) {
const unsigned = name === 'bool'
|| name === 'byte'
|| name === 'size_t'
|| name[0] === 'u';
const {size, alignment} = getAlignmentAndSize(nativeRef, name)
assert(alignment > 0);

const max_size_is_8 = name === 'long'
|| name === 'ulong'
|| name === 'longlong'
|| name === 'ulonglong'
|| name === 'size_t'
|| name === 'ssize_t'
|| name === 'intptr_t'
|| name === 'uintptr_t'
|| size === 8
let zero_value = 0
let negative_one_value = -1
if (name === 'bool') {
zero_value = false
negative_one_value = true
} else if (max_size_is_8) {
zero_value = 0n
if (unsigned) {
negative_one_value = 1n << (BigInt(size) * 8n)
} else {
negative_one_value = -1n
}
}
let getFunctions = getFunctionsGenerate(max_size_is_8, unsigned, size)
let setFunctions = setFunctionsGenerate(max_size_is_8, unsigned, size)
overrideGetSetFunctions(name, unsigned, getFunctions, setFunctions)
// "typedef"s for the variable-sized types
const typedefTypes = [
'bool',
'byte',
'char',
'uchar',
'short',
'ushort',
'int',
'uint',
'long',
'ulong',
'longlong',
'ulonglong',
'size_t',
'ssize_t',
'intptr_t',
'uintptr_t',
];
let newType
if (typedefTypes.indexOf(name) >= 0) {
let typeName = 'int' + (size * 8);
if (unsigned) {
typeName = 'u' + typeName;
}
const type = types[typeName];
const newType = Object.create(type);
newType.get = getFunction;
newType.set = setFunction;
return newType;
newType = Object.create(types[typeName]);
} else {
return {
/**
* Attributes that may shared between types when do typedef,
* such as typedef uint8_t uchar
*/
newType = {
name: name,
size: size,
alignment: alignment,
indirection: 1,
get: getFunction,
set: setFunction,
};
}
Object.assign(newType, {
/* Different for each type */
realName: name, /* typedef name realName */
zero: zero_value,
negative_one: negative_one_value,
max_size_is_8: max_size_is_8,
}, getFunctions, setFunctions)
return newType
}

// the built-in "types"
Expand Down Expand Up @@ -1040,6 +1123,14 @@ types.longlong = createCType('longlong')

types.ulonglong = createCType('ulonglong')

/**
* The `ssize_t` type.
*
* @name ssize_t
*/

types.ssize_t = createCType('ssize_t')

/**
* The `size_t` type.
*
Expand All @@ -1048,6 +1139,20 @@ types.ulonglong = createCType('ulonglong')

types.size_t = createCType('size_t')

/**
* The `intptr_t` type.
*
* @name intptr_t
*/
types.intptr_t = createCType('intptr_t')

/**
* The `uintptr_t` type.
*
* @name uintptr_t
*/
types.uintptr_t = createCType('uintptr_t')

/**
* The `char *` type is used by "allocCString()"
* @name charPtr
Expand Down

0 comments on commit a4da340

Please sign in to comment.