lgq
2026-03-31 e491cdb48129752324c4e3764f99bd9203c56dec
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
//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