//build 20240222 //quickjs 标准库,提供和操作系统相关函数,提供标准IO相关函数 import * as os from "os" import * as std from "std" import common from "./dxCommon.js" const dxstd = {} /** * 退出应用 * @param {number} n 退出码 */ dxstd.exit = function (n) { return std.exit(n); } /** * 启动计时器,延时异步执行函数 * @param {function} func 需要执行的函数 * @param {number} delay 延迟的时间(毫秒) * @returns timer引用 */ dxstd.setTimeout = function (func, delay) { return os.setTimeout(func, delay) } /** * 清除指定的计时器 * @param {*} handle timer引用 */ dxstd.clearTimeout = function (handle) { os.clearTimeout(handle) } // 记录定时器id,用于clear,只能在同一线程中clear let allTimerIdsMap = {} /** * interval定时器 * @param {function} callback 回调函数,必填 * @param {number} interval 间隔时间,必填 * @param {boolean} once 创建后立即执行一次,非必填 * @param {number} timerId 定时器id,非必填 */ dxstd.setInterval = function (callback, interval, once, timerId) { if (timerId === null || timerId === undefined) { timerId = new Date().getTime() + "_" + this.genRandomStr(5) allTimerIdsMap[timerId] = "ready" } if (once === true) { // 创建后立即执行一次 os.setTimeout(() => { if (allTimerIdsMap[timerId]) { callback() } }, 0); } if (!allTimerIdsMap[timerId]) { return } allTimerIdsMap[timerId] = os.setTimeout(() => { if (allTimerIdsMap[timerId]) { callback() this.setInterval(callback, interval, false, timerId) } }, interval); return timerId } /** * 清除interval定时器 * @param {number} timerId 定时器id,必填 */ dxstd.clearInterval = function (timerId) { const timer = allTimerIdsMap[timerId]; if (timer) { os.clearTimeout(timer); delete allTimerIdsMap[timerId]; } } /** * 删除当前线程所有interval定时器,注意:只是删除当前线程创建的定时器,若有多个线程,每个线程都需要调用删除 */ dxstd.clearIntervalAll = function () { for (let timerId in allTimerIdsMap) { if (allTimerIdsMap.hasOwnProperty(timerId)) { os.clearTimeout(allTimerIdsMap[timerId]); delete allTimerIdsMap[timerId]; } } } /** * 生成指定长度的字母和数字组合的随机字符串 * @param {number} length 字符串长度,非必填,缺省是6 * @returns */ dxstd.genRandomStr = function (length = 6) { const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' let result = '' for (let i = 0; i < length; i++) { const randomIndex = Math.floor(Math.random() * charset.length) result += charset.charAt(randomIndex) } return result } /** * 把一段字符串作为 javascript 脚本执行 * @param {string} str js脚本字符串 * @param {boolean} async 默认为false,如果为 true,脚本中将接受 await 并返回一个 Promise */ dxstd.eval = function (str, async) { return std.evalScript(str, { async: async }); } /** * 加载一个文件内容作为 javascript 脚本执行 * @param {string} filename js脚本内容的文件名(绝对路径) */ dxstd.loadScript = function (filename) { return std.loadScript(filename); } /** * 加载文件,读取文件里的内容(使用utf) * @param {string} filename 文件名 */ dxstd.loadFile = function (filename) { return std.loadFile(filename) } /** * 保存字符串到文件 * @param {string} filename * @param {string} content */ dxstd.saveFile = function (filename, content) { if (!content || (typeof content) != 'string') { throw new Error("The 'content' value should be string and not empty") } if (!filename) { throw new Error("The 'filename' should not be empty") } if (!this.exist(filename)) { this.ensurePathExists(filename) let fd = os.open(filename, os.O_RDWR | os.O_CREAT | os.O_TRUNC); if (fd < 0) { throw new Error("Create file failed:" + filename) } os.close(fd) } let fd = std.open(filename, "w"); fd.puts(content) fd.flush(); fd.close(); common.systemBrief('sync') return true } /** * 确保文件对应的目录都存在,不存在就会创建 * @param {string} filename */ dxstd.ensurePathExists = function (filename) { const pathSegments = filename.split('/'); let currentPath = ''; for (let i = 0; i < pathSegments.length - 1; i++) { currentPath += pathSegments[i] + '/'; if (!this.exist(currentPath)) { this.mkdir(currentPath); } } } /** * 判断文件是否存在 * @param {string} filename 文件名 * @returns true/false */ dxstd.exist = function (filename) { return (os.stat(filename)[1] === 0) } /** * 返回一个包含环境变量的键值对的对象。 */ dxstd.getenviron = function () { return std.getenviron(); } /** * 返回环境变量名称的值,如果未定义则返回undefined * @param {string} name 变量名 */ dxstd.getenv = function (name) { return std.getenv(name); } /** * 将环境变量名的值设置为字符串值 * @param {string} name 变量名 * @param {string} value 变量值 */ dxstd.setenv = function (name, value) { return std.setenv(name, value); } /** * 删除环境变量 * @param {string} name 变量名 */ dxstd.unsetenv = function (name) { return std.unsetenv(name); } /** * 使用JSON.parse的超集来解析字符串。可以解析非标准的 JSON 字符串。接受以下扩展: * - 单行和多行注释 * - 未加引号的属性(仅ASCII字符的JavaScript标识符) * - 数组和对象最后可以加逗号 * - 单引号字符串 * - \f 和 \v 被接受为空格字符 * - 数字中的前面可以有加号 * - 八进制(0o前缀)和十六进制(0x前缀)数字 * @param {string} str json字符串 */ dxstd.parseExtJSON = function (str) { return std.parseExtJSON(str); } /** * 休眠delay_ms毫秒 */ dxstd.sleep = function (delay_ms) { return os.sleep(delay_ms); } /** * 返回表示平台的字符串:"linux"、"darwin"、"win32" 或 "js"。 */ dxstd.platform = function () { return os.platform; } /** * 创建一个新线程(worker)的构造函数,其API接近于WebWorkers。 * 对于动态导入的模块,它相对于当前脚本或模块路径。线程通常不共享任何数据,可以通过dxMap,dxQueue,dxWpc来共享和传递数据。不支持嵌套的 worker。 * @param {string} module_filename 指定在新创建的线程中执行的模块文件名 */ dxstd.Worker = function (module_filename) { return new os.Worker(module_filename) } dxstd.O_RDONLY = os.O_RDONLY dxstd.O_WRONLY = os.O_WRONLY dxstd.O_RDWR = os.O_RDWR dxstd.O_APPEND = os.O_APPEND dxstd.O_CREAT = os.O_CREAT dxstd.O_EXCL = os.O_EXCL dxstd.O_TRUNC = os.O_TRUNC /** * 打开一个文件。返回一个句柄,如果出现错误则返回 < 0。 * @param {string} filename 文件绝对路径 * @param {number} flags O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_CREAT,O_EXCL,O_TRUNC * 1. O_RDONLY :以只读方式打开文件 * 2. O_WRONLY :以只写方式打开文件 * 3. O_RDWR :以可读可写方式打开文件 * 以上三个是文件访问权限标志,传入的flags 参数中必须要包含其中一种标志,而且只能包含一种,打开的文件只能按照这种权限来操作, 譬如使用了 O_RDONLY 标志,就只能对文件进行读取操作,不能写操作。 * 4. O_APPEND :调用 open 函数打开文件,当每次使用 write()函数对文件进行写操作时,都会自动把文件当前位置偏移量移动到文件末尾, 从文件末尾开始写入数据,也就是意味着每次写入数据都是从文件末尾开始。 O_APPEND标志并不会影响读文件,当读取文件时, O_APPEND 标志并不会影响读位置偏移量, 即使使用了 O_APPEND标志,读文件位置偏移量默认情况下依然是文件头, 使用 lseek 函数来改变 write()时的写位置偏移量也不会成功, 当执行 write()函数时,检测到 open 函数携带了 O_APPEND 标志,所以在 write 函数内部会自动将写位置偏移量移动到文件末尾 * 5. O_CREAT:如果 filename 参数指向的文件不存在则创建此文件 * 6. O_EXCL :此标志一般结合 O_CREAT 标志一起使用,用于专门创建文件。 在 flags 参数同时使用到了 O_CREAT 和O_EXCL 标志的情况下,如果 filename 参数指向的文件已经存在, 则 open 函数返回错误。可以用于测试一个文件是否存在,如果不存在则创建此文件,如果存在则返回错误,这使得测试和创建两者成为一个原子操作。 * 7. O_TRUNC :调用 open 函数打开文件的时候会将文件原本的内容全部丢弃,文件大小变为 0; */ dxstd.open = function (filename, flags) { return os.open(filename, flags); } /** * 判断给定路径是否是一个文件夹。 * @param {string} filename - 要检查的路径。 * @returns {boolean} 如果是文件夹则返回 true,否则返回 false,如果不存在,抛出异常。 */ dxstd.isDir = function (filename) { let stat = os.stat(filename) if (stat[1] != 0) { throw new Error("No such file:" + filename) } return ((stat[0].mode & this.S_IFMT) === this.S_IFDIR); } /** * 关闭文件 * @param {*} fd 文件句柄 */ dxstd.close = function (fd) { return os.close(fd) } dxstd.SEEK_SET = std.SEEK_SET dxstd.SEEK_CUR = std.SEEK_CUR dxstd.SEEK_END = std.SEEK_END /** * 在文件中进行定位。使用SEEK_*来表示whence。offset可以是数字或bigint。如果offset是bigint,则返回一个bigint。 * @param {*} fd 文件句柄 * @param {number} offset 为偏移量,整数表示正向偏移,负数表示负向偏移 * @param {*} whence 设定从文件的哪里开始偏移: SEEK_SET: 文件开头;SEEK_CUR: 当前位置;SEEK_END: 文件结尾 */ dxstd.seek = function (fd, offset, whence) { return os.seek(fd, offset, whence) } /** * 从文件句柄fd读取length字节到位于字节位置offset的ArrayBuffer缓冲区。返回读取的字节数,如果出现错误则返回 < 0。 * @param {*} fd 文件句柄 * @param {*} buffer ArrayBuffer对象 * @param {number} offset 偏移量 * @param {number} length 读取的字节长度 */ dxstd.read = function (fd, buffer, offset, length) { return os.read(fd, buffer, offset, length); } /** * 从ArrayBuffer缓冲区的字节位置offset向文件句柄fd写入length字节。返回已写入的字节数,如果出现错误则返回 < 0。 * @param {*} fd 文件句柄 * @param {*} buffer ArrayBuffer对象 * @param {*} offset 偏移量 * @param {*} length 写的字节长度 */ dxstd.write = function (fd, buffer, offset, length) { return os.write(fd, buffer, offset, length); } /** * 删除文件,成功返回0否则-errno * @param {string} filename 文件绝对路径 */ dxstd.remove = function (filename) { return os.remove(filename) } /** * 修改文件名称,成功返回0否则-errno * @param {string} oldname 旧文件绝对路径 * @param {string} newname 新文件绝对路径 */ dxstd.rename = function (oldname, newname) { return os.rename(oldname, newname) } /** * 返回 [str, err],其中 str 是当前工作目录,err 是错误代码 */ dxstd.getcwd = function () { return os.getcwd() } /** * 改变当前工作目录 * @param {string} path 目录,支持绝对和相对路径 */ dxstd.chdir = function (path) { return os.chdir(path) } /** * 创建目录,成功返回0否则-errno * @param {string} path 目录绝对路径 */ dxstd.mkdir = function (path) { return os.mkdir(path) } dxstd.S_IFMT = os.S_IFMT dxstd.S_IFIFO = os.S_IFIFO dxstd.S_IFCHR = os.S_IFCHR dxstd.S_IFDIR = os.S_IFDIR dxstd.S_IFBLK = os.S_IFBLK dxstd.S_IFREG = os.S_IFREG dxstd.S_IFSOCK = os.S_IFSOCK dxstd.S_IFLNK = os.S_IFLNK dxstd.S_ISGID = os.S_ISGID dxstd.S_ISUID = os.S_ISUID /** * 返回 [obj, err],其中 obj 是一个包含路径path的文件状态信息的对象。 * err 是错误代码。obj 中定义了以下字段:dev、ino、mode、nlink、uid、gid、rdev、size、blocks、atime、mtime、ctime。 * 时间以自1970年以来的毫秒为单位指定。 * 其中mode的值对应以下枚举,例如,检查一个文件是否是目录可以使用 (mode & S_IFMT) == S_IFDIR 的方式: S_IFMT:位掩码,用于提取文件类型部分的位。这是一个用于屏蔽文件类型位的常量。 S_IFIFO:表示FIFO(命名管道)。 S_IFCHR:表示字符设备。 S_IFDIR:表示目录。 S_IFBLK:表示块设备。 S_IFREG:表示常规文件。 S_IFSOCK:表示套接字。 S_IFLNK:表示符号链接。 S_ISGID:设置组ID位。 S_ISUID:设置用户ID位。 * @param {string} path 文件或目录绝对路径 */ dxstd.stat = function (path) { return os.stat(path) } /** * lstat() 与 stat() 相同,只是它返回关于链接本身的信息。 * @param {*} path 文件或目录绝对路径 */ dxstd.lstat = function (path) { return os.lstat(path) } /** * 返回 [array, err],其中 array 是包含目录路径下的文件名的字符串数组。err 是错误代码。 * @param {string} path 目录绝对路径 */ dxstd.readdir = function (path) { return os.readdir(path) } export default dxstd