( db: Database, path: string, bytes: Uint8Array, dirtyRanges: WriteFileRange[], options: WriteFileOptions, now: () => number, )
| 994 | } |
| 995 | |
| 996 | export function writeFileRangesSync( |
| 997 | db: Database, |
| 998 | path: string, |
| 999 | bytes: Uint8Array, |
| 1000 | dirtyRanges: WriteFileRange[], |
| 1001 | options: WriteFileOptions, |
| 1002 | now: () => number, |
| 1003 | ): void { |
| 1004 | const { parts, path: canonical } = canonicalizePath(path); |
| 1005 | if (parts.length === 0) { |
| 1006 | throw createWorkspaceError("EISDIR", "cannot write to the root directory", canonical); |
| 1007 | } |
| 1008 | assertNotReadOnly(db, canonical); |
| 1009 | const mode = (options.mode ?? 0o644) & 0o7777; |
| 1010 | const ranges = normalizeRanges(dirtyRanges, bytes.byteLength); |
| 1011 | const mtime = now(); |
| 1012 | db.transactionSync(() => { |
| 1013 | const parentInode = resolveParent(db, parts, canonical); |
| 1014 | const leafName = parts[parts.length - 1]; |
| 1015 | const existing = db.one<{ child_inode: number }>( |
| 1016 | "SELECT child_inode FROM vfs_dirents WHERE parent_inode = ? AND name = ?", |
| 1017 | parentInode, |
| 1018 | leafName, |
| 1019 | ); |
| 1020 | |
| 1021 | let inode: number; |
| 1022 | let oldChunks: ChunkRef[] = []; |
| 1023 | if (existing !== undefined) { |
| 1024 | const node = db.one<{ type: "file" | "dir" }>( |
| 1025 | "SELECT type FROM vfs_nodes WHERE inode = ?", |
| 1026 | existing.child_inode, |
| 1027 | ); |
| 1028 | if (node?.type === "dir") { |
| 1029 | throw createWorkspaceError("EISDIR", `path is a directory: ${canonical}`, canonical); |
| 1030 | } |
| 1031 | inode = existing.child_inode; |
| 1032 | oldChunks = existingChunkRefs(db, inode); |
| 1033 | } else { |
| 1034 | inode = insertFileNode(db, mode, mtime); |
| 1035 | insertFileDirent(db, parentInode, leafName, inode, canonical); |
| 1036 | } |
| 1037 | |
| 1038 | const rev = incrementRev(db); |
| 1039 | const nextChunks: ChunkRef[] = []; |
| 1040 | const chunkCount = Math.ceil(bytes.byteLength / CHUNK_SIZE); |
| 1041 | for (let idx = 0; idx < chunkCount; idx++) { |
| 1042 | const start = idx * CHUNK_SIZE; |
| 1043 | const end = Math.min(start + CHUNK_SIZE, bytes.byteLength); |
| 1044 | const size = end - start; |
| 1045 | const oldChunk = oldChunks[idx]; |
| 1046 | if (oldChunk !== undefined && oldChunk.size === size && !rangesOverlap(start, end, ranges)) { |
| 1047 | nextChunks.push(oldChunk); |
| 1048 | continue; |
| 1049 | } |
| 1050 | const chunk = { |
| 1051 | hash: sha256(bytes.subarray(start, end)), |
| 1052 | bytes: bytes.subarray(start, end), |
| 1053 | size, |
no test coverage detected