nano-git 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +407 -0
- package/dist/backend/file-backend.d.mts +26 -0
- package/dist/backend/file-backend.mjs +83 -0
- package/dist/backend/file.d.mts +2 -0
- package/dist/backend/file.mjs +2 -0
- package/dist/backend/index.d.mts +2 -0
- package/dist/backend/index.mjs +1 -0
- package/dist/backend/memory-backend.d.mts +25 -0
- package/dist/backend/memory-backend.mjs +33 -0
- package/dist/backend/memory.d.mts +2 -0
- package/dist/backend/memory.mjs +2 -0
- package/dist/backend/types.d.mts +90 -0
- package/dist/core/errors.d.mts +124 -0
- package/dist/core/errors.mjs +168 -0
- package/dist/core/hash-digest.d.mts +35 -0
- package/dist/core/hash-digest.mjs +50 -0
- package/dist/core/hash-file.d.mts +20 -0
- package/dist/core/hash-file.mjs +27 -0
- package/dist/core/hash-path.d.mts +17 -0
- package/dist/core/hash-path.mjs +35 -0
- package/dist/core/types/odb.d.mts +63 -0
- package/dist/core/types/refs.d.mts +140 -0
- package/dist/core/types/refs.mjs +19 -0
- package/dist/core/types/shallow.d.mts +52 -0
- package/dist/core/types.d.mts +154 -0
- package/dist/core/types.mjs +43 -0
- package/dist/errors.d.mts +2 -0
- package/dist/errors.mjs +2 -0
- package/dist/hash-file.d.mts +2 -0
- package/dist/hash-file.mjs +2 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.mjs +14 -0
- package/dist/objects/author.d.mts +25 -0
- package/dist/objects/author.mjs +45 -0
- package/dist/objects/blob.d.mts +15 -0
- package/dist/objects/blob.mjs +20 -0
- package/dist/objects/codec.d.mts +46 -0
- package/dist/objects/codec.mjs +84 -0
- package/dist/objects/commit.d.mts +26 -0
- package/dist/objects/commit.mjs +160 -0
- package/dist/objects/index.d.mts +8 -0
- package/dist/objects/index.mjs +8 -0
- package/dist/objects/raw.d.mts +85 -0
- package/dist/objects/raw.mjs +111 -0
- package/dist/objects/tag.d.mts +26 -0
- package/dist/objects/tag.mjs +148 -0
- package/dist/objects/tree.d.mts +22 -0
- package/dist/objects/tree.mjs +66 -0
- package/dist/odb/file-utils.mjs +136 -0
- package/dist/odb/file.d.mts +22 -0
- package/dist/odb/file.mjs +66 -0
- package/dist/odb/memory.d.mts +22 -0
- package/dist/odb/memory.mjs +54 -0
- package/dist/pack/composite-store.d.mts +76 -0
- package/dist/pack/composite-store.mjs +133 -0
- package/dist/pack/constants.mjs +48 -0
- package/dist/pack/crc32.mjs +38 -0
- package/dist/pack/delta-apply.d.mts +21 -0
- package/dist/pack/delta-apply.mjs +71 -0
- package/dist/pack/delta-create.d.mts +28 -0
- package/dist/pack/delta-create.mjs +151 -0
- package/dist/pack/index.d.mts +16 -0
- package/dist/pack/index.mjs +13 -0
- package/dist/pack/object-header.d.mts +36 -0
- package/dist/pack/object-header.mjs +72 -0
- package/dist/pack/ofs-delta-offset.d.mts +35 -0
- package/dist/pack/ofs-delta-offset.mjs +59 -0
- package/dist/pack/pack-builder-types.d.mts +19 -0
- package/dist/pack/pack-builder.d.mts +52 -0
- package/dist/pack/pack-builder.mjs +80 -0
- package/dist/pack/pack-encoding.mjs +76 -0
- package/dist/pack/pack-index-reader.d.mts +57 -0
- package/dist/pack/pack-index-reader.mjs +90 -0
- package/dist/pack/pack-index-types.d.mts +16 -0
- package/dist/pack/pack-index-writer.d.mts +45 -0
- package/dist/pack/pack-index-writer.mjs +106 -0
- package/dist/pack/pack-reader-resolver.mjs +104 -0
- package/dist/pack/pack-reader-types.d.mts +31 -0
- package/dist/pack/pack-reader-types.mjs +22 -0
- package/dist/pack/pack-reader-utils.mjs +61 -0
- package/dist/pack/pack-reader.d.mts +81 -0
- package/dist/pack/pack-reader.mjs +171 -0
- package/dist/pack/pack-store-loader.mjs +79 -0
- package/dist/pack/pack-store-types.d.mts +16 -0
- package/dist/pack/pack-store.d.mts +55 -0
- package/dist/pack/pack-store.mjs +114 -0
- package/dist/pack/pack-writer.mjs +96 -0
- package/dist/pack/varint.d.mts +34 -0
- package/dist/pack/varint.mjs +57 -0
- package/dist/refs/file.d.mts +6 -0
- package/dist/refs/file.mjs +222 -0
- package/dist/refs/fs-utils.mjs +30 -0
- package/dist/refs/memory.d.mts +14 -0
- package/dist/refs/memory.mjs +104 -0
- package/dist/refs/names.d.mts +57 -0
- package/dist/refs/names.mjs +80 -0
- package/dist/refs/resolve.d.mts +33 -0
- package/dist/refs/resolve.mjs +55 -0
- package/dist/refs/shallow/file.d.mts +17 -0
- package/dist/refs/shallow/file.mjs +61 -0
- package/dist/refs/shallow/memory.d.mts +18 -0
- package/dist/refs/shallow/memory.mjs +33 -0
- package/dist/repository/core.d.mts +3 -0
- package/dist/repository/core.mjs +2 -0
- package/dist/repository/create.d.mts +22 -0
- package/dist/repository/create.mjs +43 -0
- package/dist/repository/file.d.mts +43 -0
- package/dist/repository/file.mjs +82 -0
- package/dist/repository/import/import-glob.mjs +44 -0
- package/dist/repository/import/import-plan-builder.mjs +625 -0
- package/dist/repository/import/import-session-types.d.mts +280 -0
- package/dist/repository/import/import-session.mjs +96 -0
- package/dist/repository/import/import-view.mjs +133 -0
- package/dist/repository/memory.d.mts +17 -0
- package/dist/repository/memory.mjs +33 -0
- package/dist/repository/ops/fetch-operations.mjs +20 -0
- package/dist/repository/ops/fetch-types.d.mts +82 -0
- package/dist/repository/ops/fetch-url.mjs +82 -0
- package/dist/repository/ops/fs-object-operations.mjs +31 -0
- package/dist/repository/ops/maintenance-operations.mjs +62 -0
- package/dist/repository/ops/maintenance-types.d.mts +42 -0
- package/dist/repository/ops/object-operations.mjs +65 -0
- package/dist/repository/ops/object-types.d.mts +109 -0
- package/dist/repository/ops/push-operations.mjs +16 -0
- package/dist/repository/ops/push-resolution.mjs +17 -0
- package/dist/repository/ops/push-types.d.mts +72 -0
- package/dist/repository/ops/push-url.mjs +45 -0
- package/dist/repository/ops/reachability.mjs +60 -0
- package/dist/repository/ops/ref-operations.mjs +86 -0
- package/dist/repository/ops/ref-types.d.mts +70 -0
- package/dist/repository/tree/tree-patch.d.mts +64 -0
- package/dist/repository/tree/tree-patch.mjs +268 -0
- package/dist/repository/tree/tree-walk.d.mts +46 -0
- package/dist/repository/tree/tree-walk.mjs +65 -0
- package/dist/repository/tree/tree-writer.mjs +68 -0
- package/dist/repository/types.d.mts +36 -0
- package/dist/sha1.d.mts +4 -0
- package/dist/sha1.mjs +4 -0
- package/dist/transport/client/receive-pack/http.d.mts +33 -0
- package/dist/transport/client/receive-pack/http.mjs +99 -0
- package/dist/transport/client/receive-pack/push-error.d.mts +23 -0
- package/dist/transport/client/receive-pack/push-error.mjs +32 -0
- package/dist/transport/client/receive-pack/push-pack-plan.d.mts +28 -0
- package/dist/transport/client/receive-pack/push-pack-plan.mjs +60 -0
- package/dist/transport/client/receive-pack/push-policy.d.mts +19 -0
- package/dist/transport/client/receive-pack/push-policy.mjs +64 -0
- package/dist/transport/client/receive-pack/push-ref-plan.d.mts +45 -0
- package/dist/transport/client/receive-pack/push-ref-plan.mjs +108 -0
- package/dist/transport/client/receive-pack/push-report.d.mts +28 -0
- package/dist/transport/client/receive-pack/push-report.mjs +84 -0
- package/dist/transport/client/receive-pack/push-request-plan.mjs +52 -0
- package/dist/transport/client/receive-pack/push.d.mts +32 -0
- package/dist/transport/client/receive-pack/push.mjs +97 -0
- package/dist/transport/client/receive-pack/request.d.mts +39 -0
- package/dist/transport/client/receive-pack/request.mjs +46 -0
- package/dist/transport/client/receive-pack/response.d.mts +26 -0
- package/dist/transport/client/receive-pack/response.mjs +52 -0
- package/dist/transport/client/receive-pack/result.d.mts +34 -0
- package/dist/transport/client/receive-pack/result.mjs +100 -0
- package/dist/transport/client/upload-pack/capability-advertisement.d.mts +56 -0
- package/dist/transport/client/upload-pack/capability-advertisement.mjs +130 -0
- package/dist/transport/client/upload-pack/fetch.d.mts +109 -0
- package/dist/transport/client/upload-pack/fetch.mjs +392 -0
- package/dist/transport/client/upload-pack/http.d.mts +29 -0
- package/dist/transport/client/upload-pack/http.mjs +79 -0
- package/dist/transport/client/upload-pack/ls-refs.d.mts +75 -0
- package/dist/transport/client/upload-pack/ls-refs.mjs +150 -0
- package/dist/transport/client/upload-pack/object-info.d.mts +65 -0
- package/dist/transport/client/upload-pack/object-info.mjs +111 -0
- package/dist/transport/client/upload-pack/types.d.mts +153 -0
- package/dist/transport/http/index.d.mts +3 -0
- package/dist/transport/http/index.mjs +2 -0
- package/dist/transport/http/smart-http.d.mts +46 -0
- package/dist/transport/http/smart-http.mjs +176 -0
- package/dist/transport/http/types.d.mts +27 -0
- package/dist/transport/index.d.mts +9 -0
- package/dist/transport/index.mjs +8 -0
- package/dist/transport/protocol/object-graph.d.mts +63 -0
- package/dist/transport/protocol/object-graph.mjs +149 -0
- package/dist/transport/protocol/pkt-line.d.mts +109 -0
- package/dist/transport/protocol/pkt-line.mjs +195 -0
- package/dist/transport/protocol/ref-advertisement.mjs +185 -0
- package/dist/transport/protocol/ref-collection.d.mts +38 -0
- package/dist/transport/protocol/ref-collection.mjs +63 -0
- package/dist/transport/protocol/ref-match.d.mts +39 -0
- package/dist/transport/protocol/ref-match.mjs +42 -0
- package/dist/transport/protocol/refspec.d.mts +44 -0
- package/dist/transport/protocol/refspec.mjs +79 -0
- package/dist/transport/protocol/side-band.d.mts +65 -0
- package/dist/transport/protocol/side-band.mjs +142 -0
- package/dist/transport/protocol/transport-capabilities.mjs +46 -0
- package/dist/transport/protocol/types.d.mts +148 -0
- package/dist/transport/protocol/update-refs.d.mts +68 -0
- package/dist/transport/protocol/update-refs.mjs +126 -0
- package/dist/transport/receive-pack.d.mts +11 -0
- package/dist/transport/receive-pack.mjs +11 -0
- package/dist/transport/server/receive-pack/advertise.d.mts +28 -0
- package/dist/transport/server/receive-pack/advertise.mjs +88 -0
- package/dist/transport/server/receive-pack/handler.d.mts +30 -0
- package/dist/transport/server/receive-pack/handler.mjs +156 -0
- package/dist/transport/server/receive-pack/index.d.mts +6 -0
- package/dist/transport/server/receive-pack/index.mjs +6 -0
- package/dist/transport/server/receive-pack/parse.d.mts +17 -0
- package/dist/transport/server/receive-pack/parse.mjs +80 -0
- package/dist/transport/server/receive-pack/report-status.mjs +64 -0
- package/dist/transport/server/receive-pack/service.d.mts +41 -0
- package/dist/transport/server/receive-pack/service.mjs +39 -0
- package/dist/transport/server/receive-pack/types.d.mts +56 -0
- package/dist/transport/server/receive-pack/types.mjs +25 -0
- package/dist/transport/server/receive-pack/unpack.mjs +119 -0
- package/dist/transport/server/upload-pack/advertise.d.mts +20 -0
- package/dist/transport/server/upload-pack/advertise.mjs +30 -0
- package/dist/transport/server/upload-pack/command.d.mts +43 -0
- package/dist/transport/server/upload-pack/command.mjs +56 -0
- package/dist/transport/server/upload-pack/fetch.d.mts +43 -0
- package/dist/transport/server/upload-pack/fetch.mjs +217 -0
- package/dist/transport/server/upload-pack/index.d.mts +7 -0
- package/dist/transport/server/upload-pack/index.mjs +7 -0
- package/dist/transport/server/upload-pack/ls-refs.d.mts +38 -0
- package/dist/transport/server/upload-pack/ls-refs.mjs +113 -0
- package/dist/transport/server/upload-pack/service.d.mts +40 -0
- package/dist/transport/server/upload-pack/service.mjs +51 -0
- package/dist/transport/server/upload-pack/types.d.mts +11 -0
- package/dist/transport/server/upload-pack/types.mjs +21 -0
- package/dist/transport/upload-pack.d.mts +7 -0
- package/dist/transport/upload-pack.mjs +6 -0
- package/package.json +98 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { InvalidObjectError } from "../core/errors.mjs";
|
|
2
|
+
import { assertObjectType, sha1 } from "../core/types.mjs";
|
|
3
|
+
import { hashToPath } from "../core/hash-path.mjs";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { deflateSync, inflateSync } from "node:zlib";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
//#region src/odb/file-utils.ts
|
|
8
|
+
/**
|
|
9
|
+
* loose object 文件系统辅助函数
|
|
10
|
+
*
|
|
11
|
+
* 负责对象路径计算、压缩读写与对象目录枚举。
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* 计算 loose object 文件路径
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const path = getLooseObjectPath("/repo/.git/objects", hash);
|
|
19
|
+
* console.log(path);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
function getLooseObjectPath(objectsDir, hash) {
|
|
23
|
+
return join(objectsDir, hashToPath(hash));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 检查 loose object 是否存在
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* if (hasLooseObject(objectsDir, hash)) {
|
|
31
|
+
* console.log("对象已存在");
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
function hasLooseObject(objectsDir, hash) {
|
|
36
|
+
return existsSync(getLooseObjectPath(objectsDir, hash));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 删除 loose object 文件
|
|
40
|
+
*
|
|
41
|
+
* 删除不存在的对象时静默成功(no-op)。
|
|
42
|
+
*
|
|
43
|
+
* @param objectsDir - .git/objects 目录路径
|
|
44
|
+
* @param hash - 要删除的对象哈希
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* deleteLooseObject(objectsDir, hash);
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
function deleteLooseObject(objectsDir, hash) {
|
|
52
|
+
const objectPath = getLooseObjectPath(objectsDir, hash);
|
|
53
|
+
if (existsSync(objectPath)) unlinkSync(objectPath);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 枚举所有 loose object 哈希
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const hashes = listLooseObjects(objectsDir);
|
|
61
|
+
* console.log(hashes.length);
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function listLooseObjects(objectsDir) {
|
|
65
|
+
if (!existsSync(objectsDir)) return [];
|
|
66
|
+
const hashes = [];
|
|
67
|
+
const dirs = readdirSync(objectsDir).sort();
|
|
68
|
+
for (const dirName of dirs) {
|
|
69
|
+
if (dirName === "info" || dirName === "pack" || dirName.length !== 2) continue;
|
|
70
|
+
const dirPath = join(objectsDir, dirName);
|
|
71
|
+
if (!statSync(dirPath).isDirectory()) continue;
|
|
72
|
+
const files = readdirSync(dirPath).sort();
|
|
73
|
+
for (const fileName of files) {
|
|
74
|
+
if (fileName.length !== 38) continue;
|
|
75
|
+
hashes.push(sha1(`${dirName}${fileName}`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return hashes;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 写入 raw loose object 文件(使用 canonical bytes)
|
|
82
|
+
*
|
|
83
|
+
* 直接以 "<type> <size>\0<content>" 格式 deflate 写入,
|
|
84
|
+
* 不经过语义序列化。
|
|
85
|
+
*
|
|
86
|
+
* @param objectsDir - .git/objects 目录路径
|
|
87
|
+
* @param raw - 原始对象
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* writeRawLooseObject(objectsDir, raw);
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
function writeRawLooseObject(objectsDir, raw) {
|
|
95
|
+
const objectPath = getLooseObjectPath(objectsDir, raw.hash);
|
|
96
|
+
mkdirSync(join(objectsDir, raw.hash.slice(0, 2)), { recursive: true });
|
|
97
|
+
const header = `${raw.type} ${raw.content.length}\0`;
|
|
98
|
+
writeFileSync(objectPath, deflateSync(Buffer.concat([Buffer.from(header), raw.content])));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 读取 raw loose object 文件
|
|
102
|
+
*
|
|
103
|
+
* 读取后直接返回 { hash, type, content },
|
|
104
|
+
* 不经过语义反序列化。
|
|
105
|
+
*
|
|
106
|
+
* @param objectsDir - .git/objects 目录路径
|
|
107
|
+
* @param hash - 对象哈希
|
|
108
|
+
* @returns 原始对象
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* const raw = readRawLooseObject(objectsDir, hash);
|
|
113
|
+
* console.log(raw.type, raw.content.length);
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
function readRawLooseObject(objectsDir, hash) {
|
|
117
|
+
const objectPath = getLooseObjectPath(objectsDir, hash);
|
|
118
|
+
if (!existsSync(objectPath)) throw new Error(`Object not found: ${hash}`);
|
|
119
|
+
const data = inflateSync(readFileSync(objectPath));
|
|
120
|
+
const nullIndex = data.indexOf(0);
|
|
121
|
+
if (nullIndex === -1) throw new InvalidObjectError("missing null byte in loose object");
|
|
122
|
+
const header = data.subarray(0, nullIndex).toString("utf-8");
|
|
123
|
+
const match = header.match(/^(blob|tree|commit|tag) (\d+)$/);
|
|
124
|
+
if (!match) throw new InvalidObjectError(`invalid loose object header: ${header}`);
|
|
125
|
+
const type = assertObjectType(match[1]);
|
|
126
|
+
const size = parseInt(match[2], 10);
|
|
127
|
+
const content = data.subarray(nullIndex + 1);
|
|
128
|
+
if (content.length !== size) throw new InvalidObjectError(`size mismatch: header says ${size}, got ${content.length}`);
|
|
129
|
+
return {
|
|
130
|
+
hash,
|
|
131
|
+
type,
|
|
132
|
+
content
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
export { deleteLooseObject, hasLooseObject, listLooseObjects, readRawLooseObject, writeRawLooseObject };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ObjectDatabase } from "../core/types/odb.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/odb/file.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* 创建基于文件系统的对象数据库
|
|
6
|
+
*
|
|
7
|
+
* @param gitDir - .git 目录的路径
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const db = createFileObjectStore("/path/to/repo/.git");
|
|
12
|
+
*
|
|
13
|
+
* // 摄入一个原始对象
|
|
14
|
+
* db.ingest(raw);
|
|
15
|
+
*
|
|
16
|
+
* // 读取回来
|
|
17
|
+
* const obj = db.read(hash);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function createFileObjectStore(gitDir: string): ObjectDatabase;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { createFileObjectStore };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { hashObject } from "../core/hash-digest.mjs";
|
|
2
|
+
import { deleteLooseObject, hasLooseObject, listLooseObjects, readRawLooseObject, writeRawLooseObject } from "./file-utils.mjs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
//#region src/odb/file.ts
|
|
5
|
+
/**
|
|
6
|
+
* 基于文件系统的对象数据库(raw-first)
|
|
7
|
+
*
|
|
8
|
+
* Git 将对象存储在 .git/objects/ 目录下:
|
|
9
|
+
* - 每个对象以 zlib 压缩格式存储
|
|
10
|
+
* - 路径格式: .git/objects/<前2字符>/<剩余38字符>
|
|
11
|
+
* - 例如: .git/objects/95/d09f2b10159347eece71399a7e2e907ea3df4f
|
|
12
|
+
*
|
|
13
|
+
* ODB 的真实边界是 RawGitObject,不是 GitObject。
|
|
14
|
+
* 写入路径直接接收 RawGitObject,不再经过语义序列化。
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* 创建基于文件系统的对象数据库
|
|
18
|
+
*
|
|
19
|
+
* @param gitDir - .git 目录的路径
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const db = createFileObjectStore("/path/to/repo/.git");
|
|
24
|
+
*
|
|
25
|
+
* // 摄入一个原始对象
|
|
26
|
+
* db.ingest(raw);
|
|
27
|
+
*
|
|
28
|
+
* // 读取回来
|
|
29
|
+
* const obj = db.read(hash);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function createFileObjectStore(gitDir) {
|
|
33
|
+
const objectsDir = join(gitDir, "objects");
|
|
34
|
+
return {
|
|
35
|
+
ingest(raw) {
|
|
36
|
+
const expectedHash = hashObject(raw.type, raw.content);
|
|
37
|
+
if (expectedHash !== raw.hash) throw new Error(`RawGitObject hash mismatch: expected ${expectedHash}, got ${raw.hash}`);
|
|
38
|
+
if (hasLooseObject(objectsDir, raw.hash)) return;
|
|
39
|
+
writeRawLooseObject(objectsDir, raw);
|
|
40
|
+
},
|
|
41
|
+
ingestMany(objects) {
|
|
42
|
+
for (const raw of objects) this.ingest(raw);
|
|
43
|
+
},
|
|
44
|
+
read(hash) {
|
|
45
|
+
return readRawLooseObject(objectsDir, hash);
|
|
46
|
+
},
|
|
47
|
+
tryRead(hash) {
|
|
48
|
+
try {
|
|
49
|
+
return readRawLooseObject(objectsDir, hash);
|
|
50
|
+
} catch {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
exists(hash) {
|
|
55
|
+
return hasLooseObject(objectsDir, hash);
|
|
56
|
+
},
|
|
57
|
+
list() {
|
|
58
|
+
return listLooseObjects(objectsDir);
|
|
59
|
+
},
|
|
60
|
+
delete(hash) {
|
|
61
|
+
deleteLooseObject(objectsDir, hash);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { createFileObjectStore };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ObjectDatabase } from "../core/types/odb.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/odb/memory.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* 内存对象数据库接口
|
|
6
|
+
*/
|
|
7
|
+
type MemoryObjectDatabase = ObjectDatabase;
|
|
8
|
+
/**
|
|
9
|
+
* 创建基于内存的对象数据库
|
|
10
|
+
*
|
|
11
|
+
* 所有对象存储在内存中,程序退出后丢失。
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const db = createMemoryObjectStore();
|
|
16
|
+
* db.ingest(raw);
|
|
17
|
+
* const obj = db.read(hash);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function createMemoryObjectStore(): MemoryObjectDatabase;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { MemoryObjectDatabase, type ObjectDatabase, createMemoryObjectStore };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { hashObject } from "../core/hash-digest.mjs";
|
|
2
|
+
//#region src/odb/memory.ts
|
|
3
|
+
/**
|
|
4
|
+
* 基于内存的对象数据库(raw-first)
|
|
5
|
+
*
|
|
6
|
+
* 所有对象以 RawGitObject 形式存储在内存 Map 中,程序退出后丢失。
|
|
7
|
+
* 适用于单元测试和临时操作场景。
|
|
8
|
+
*
|
|
9
|
+
* ODB 的真实边界是 RawGitObject,不是 GitObject。
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* 创建基于内存的对象数据库
|
|
13
|
+
*
|
|
14
|
+
* 所有对象存储在内存中,程序退出后丢失。
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const db = createMemoryObjectStore();
|
|
19
|
+
* db.ingest(raw);
|
|
20
|
+
* const obj = db.read(hash);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function createMemoryObjectStore() {
|
|
24
|
+
const store = /* @__PURE__ */ new Map();
|
|
25
|
+
return {
|
|
26
|
+
ingest(raw) {
|
|
27
|
+
const expectedHash = hashObject(raw.type, raw.content);
|
|
28
|
+
if (expectedHash !== raw.hash) throw new Error(`RawGitObject hash mismatch: expected ${expectedHash}, got ${raw.hash}`);
|
|
29
|
+
if (!store.has(raw.hash)) store.set(raw.hash, raw);
|
|
30
|
+
},
|
|
31
|
+
ingestMany(objects) {
|
|
32
|
+
for (const raw of objects) this.ingest(raw);
|
|
33
|
+
},
|
|
34
|
+
read(hash) {
|
|
35
|
+
const obj = store.get(hash);
|
|
36
|
+
if (!obj) throw new Error(`Object not found: ${hash}`);
|
|
37
|
+
return obj;
|
|
38
|
+
},
|
|
39
|
+
tryRead(hash) {
|
|
40
|
+
return store.get(hash);
|
|
41
|
+
},
|
|
42
|
+
exists(hash) {
|
|
43
|
+
return store.has(hash);
|
|
44
|
+
},
|
|
45
|
+
list() {
|
|
46
|
+
return Array.from(store.keys());
|
|
47
|
+
},
|
|
48
|
+
delete(hash) {
|
|
49
|
+
store.delete(hash);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { createMemoryObjectStore };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { RawGitObject, SHA1 } from "../core/types.mjs";
|
|
2
|
+
import { ObjectDatabase, ObjectSource } from "../core/types/odb.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/pack/composite-store.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* 创建组合对象数据库
|
|
7
|
+
*
|
|
8
|
+
* @param primary - 主数据库(用于摄入)
|
|
9
|
+
* @param secondary - 辅助源列表(只读,按顺序查找)
|
|
10
|
+
* @returns 组合对象数据库
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const looseDb = createFileObjectStore(gitDir);
|
|
15
|
+
* const packSource = createPackObjectStore(gitDir);
|
|
16
|
+
* const db = createCompositeObjectDatabase(looseDb, packSource);
|
|
17
|
+
*
|
|
18
|
+
* // 读取时自动在所有存储中查找
|
|
19
|
+
* const raw = db.read(hash);
|
|
20
|
+
*
|
|
21
|
+
* // 摄入时只写入主数据库
|
|
22
|
+
* db.ingest(raw);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function createCompositeObjectDatabase(primary: ObjectDatabase, ...secondary: ObjectSource[]): CompositeObjectDatabase;
|
|
26
|
+
/**
|
|
27
|
+
* 组合对象数据库类
|
|
28
|
+
*/
|
|
29
|
+
declare class CompositeObjectDatabase implements ObjectDatabase {
|
|
30
|
+
private readonly primary;
|
|
31
|
+
private readonly secondary;
|
|
32
|
+
constructor(primary: ObjectDatabase, secondary: ObjectSource[]);
|
|
33
|
+
/**
|
|
34
|
+
* 摄入原始对象到主数据库
|
|
35
|
+
*/
|
|
36
|
+
ingest(raw: RawGitObject): void;
|
|
37
|
+
/**
|
|
38
|
+
* 批量摄入原始对象到主数据库
|
|
39
|
+
*/
|
|
40
|
+
ingestMany(objects: Iterable<RawGitObject>): void;
|
|
41
|
+
/**
|
|
42
|
+
* 删除对象
|
|
43
|
+
*
|
|
44
|
+
* 委托给主数据库的 delete。
|
|
45
|
+
*/
|
|
46
|
+
delete(hash: SHA1): void;
|
|
47
|
+
/**
|
|
48
|
+
* 尝试读取原始对象,不存在时返回 undefined
|
|
49
|
+
*/
|
|
50
|
+
tryRead(hash: SHA1): RawGitObject | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* 尝试从指定源中读取对象,不存在时返回 undefined
|
|
53
|
+
*/
|
|
54
|
+
private tryReadSource;
|
|
55
|
+
/**
|
|
56
|
+
* 读取原始对象
|
|
57
|
+
*
|
|
58
|
+
* 按顺序在所有存储中查找,返回第一个找到的对象。
|
|
59
|
+
* (跳过 exists() 前置检查,直接尝试 read() 以消除 N+1 双重调用)
|
|
60
|
+
*
|
|
61
|
+
* @throws ObjectNotFoundError 如果对象在所有存储中都不存在
|
|
62
|
+
*/
|
|
63
|
+
read(hash: SHA1): RawGitObject;
|
|
64
|
+
/**
|
|
65
|
+
* 检查对象是否存在
|
|
66
|
+
*/
|
|
67
|
+
exists(hash: SHA1): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* 列出所有对象哈希
|
|
70
|
+
*
|
|
71
|
+
* 主数据库排在前面,重复哈希自动去重。
|
|
72
|
+
*/
|
|
73
|
+
list(): SHA1[];
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { CompositeObjectDatabase, createCompositeObjectDatabase };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ObjectNotFoundError } from "../core/errors.mjs";
|
|
2
|
+
//#region src/pack/composite-store.ts
|
|
3
|
+
/**
|
|
4
|
+
* 组合对象源/数据库
|
|
5
|
+
*
|
|
6
|
+
* 将多个对象源/数据库组合在一起,按优先级顺序查找对象。
|
|
7
|
+
* 典型用法:loose objects(文件系统)+ packfile 存储。
|
|
8
|
+
*
|
|
9
|
+
* 查找顺序:
|
|
10
|
+
* 1. 先在主数据库中查找(最新写入/摄入的对象)
|
|
11
|
+
* 2. 再在辅助源中查找(已打包的对象)
|
|
12
|
+
*
|
|
13
|
+
* 摄入操作始终写入到主数据库。
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const db = createCompositeObjectDatabase(fileDb, packSource);
|
|
18
|
+
* const raw = db.read(hash); // 自动在所有存储中查找
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* 创建组合对象数据库
|
|
23
|
+
*
|
|
24
|
+
* @param primary - 主数据库(用于摄入)
|
|
25
|
+
* @param secondary - 辅助源列表(只读,按顺序查找)
|
|
26
|
+
* @returns 组合对象数据库
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const looseDb = createFileObjectStore(gitDir);
|
|
31
|
+
* const packSource = createPackObjectStore(gitDir);
|
|
32
|
+
* const db = createCompositeObjectDatabase(looseDb, packSource);
|
|
33
|
+
*
|
|
34
|
+
* // 读取时自动在所有存储中查找
|
|
35
|
+
* const raw = db.read(hash);
|
|
36
|
+
*
|
|
37
|
+
* // 摄入时只写入主数据库
|
|
38
|
+
* db.ingest(raw);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
function createCompositeObjectDatabase(primary, ...secondary) {
|
|
42
|
+
return new CompositeObjectDatabase(primary, secondary);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 组合对象数据库类
|
|
46
|
+
*/
|
|
47
|
+
var CompositeObjectDatabase = class {
|
|
48
|
+
primary;
|
|
49
|
+
secondary;
|
|
50
|
+
constructor(primary, secondary) {
|
|
51
|
+
this.primary = primary;
|
|
52
|
+
this.secondary = secondary;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 摄入原始对象到主数据库
|
|
56
|
+
*/
|
|
57
|
+
ingest(raw) {
|
|
58
|
+
return this.primary.ingest(raw);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 批量摄入原始对象到主数据库
|
|
62
|
+
*/
|
|
63
|
+
ingestMany(objects) {
|
|
64
|
+
for (const raw of objects) this.ingest(raw);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 删除对象
|
|
68
|
+
*
|
|
69
|
+
* 委托给主数据库的 delete。
|
|
70
|
+
*/
|
|
71
|
+
delete(hash) {
|
|
72
|
+
this.primary.delete(hash);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 尝试读取原始对象,不存在时返回 undefined
|
|
76
|
+
*/
|
|
77
|
+
tryRead(hash) {
|
|
78
|
+
const primaryResult = this.tryReadSource(this.primary, hash);
|
|
79
|
+
if (primaryResult !== void 0) return primaryResult;
|
|
80
|
+
for (const source of this.secondary) {
|
|
81
|
+
const result = this.tryReadSource(source, hash);
|
|
82
|
+
if (result !== void 0) return result;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 尝试从指定源中读取对象,不存在时返回 undefined
|
|
87
|
+
*/
|
|
88
|
+
tryReadSource(source, hash) {
|
|
89
|
+
try {
|
|
90
|
+
return source.read(hash);
|
|
91
|
+
} catch {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 读取原始对象
|
|
97
|
+
*
|
|
98
|
+
* 按顺序在所有存储中查找,返回第一个找到的对象。
|
|
99
|
+
* (跳过 exists() 前置检查,直接尝试 read() 以消除 N+1 双重调用)
|
|
100
|
+
*
|
|
101
|
+
* @throws ObjectNotFoundError 如果对象在所有存储中都不存在
|
|
102
|
+
*/
|
|
103
|
+
read(hash) {
|
|
104
|
+
const primaryResult = this.tryReadSource(this.primary, hash);
|
|
105
|
+
if (primaryResult !== void 0) return primaryResult;
|
|
106
|
+
for (const source of this.secondary) {
|
|
107
|
+
const result = this.tryReadSource(source, hash);
|
|
108
|
+
if (result !== void 0) return result;
|
|
109
|
+
}
|
|
110
|
+
throw new ObjectNotFoundError(hash);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 检查对象是否存在
|
|
114
|
+
*/
|
|
115
|
+
exists(hash) {
|
|
116
|
+
if (this.primary.exists(hash)) return true;
|
|
117
|
+
for (const source of this.secondary) if (source.exists(hash)) return true;
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 列出所有对象哈希
|
|
122
|
+
*
|
|
123
|
+
* 主数据库排在前面,重复哈希自动去重。
|
|
124
|
+
*/
|
|
125
|
+
list() {
|
|
126
|
+
const hashes = /* @__PURE__ */ new Set();
|
|
127
|
+
for (const hash of this.primary.list()) hashes.add(hash);
|
|
128
|
+
for (const source of this.secondary) for (const hash of source.list()) hashes.add(hash);
|
|
129
|
+
return Array.from(hashes).sort();
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
//#endregion
|
|
133
|
+
export { CompositeObjectDatabase, createCompositeObjectDatabase };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Packfile 魔数 "PACK" */
|
|
2
|
+
const PACK_SIGNATURE = Buffer.from("PACK");
|
|
3
|
+
/** idx v2 文件魔数 */
|
|
4
|
+
const IDX_V2_SIGNATURE = Buffer.from([
|
|
5
|
+
255,
|
|
6
|
+
116,
|
|
7
|
+
79,
|
|
8
|
+
99
|
|
9
|
+
]);
|
|
10
|
+
/** idx v2 扇出表大小:256 个 4 字节整数 */
|
|
11
|
+
const IDX_V2_FANOUT_SIZE = 256 * 4;
|
|
12
|
+
/**
|
|
13
|
+
* 将 Git 对象类型字符串转换为 Packfile 类型编号
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* objectTypeToNumber("commit") // => 1
|
|
18
|
+
* objectTypeToNumber("blob") // => 3
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function objectTypeToNumber(type) {
|
|
22
|
+
switch (type) {
|
|
23
|
+
case "commit": return 1;
|
|
24
|
+
case "tree": return 2;
|
|
25
|
+
case "blob": return 3;
|
|
26
|
+
case "tag": return 4;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 将 Packfile 类型编号转换为 Git 对象类型字符串
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* numberToObjectType(1) // => "commit"
|
|
35
|
+
* numberToObjectType(3) // => "blob"
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
function numberToObjectType(n) {
|
|
39
|
+
switch (n) {
|
|
40
|
+
case 1: return "commit";
|
|
41
|
+
case 2: return "tree";
|
|
42
|
+
case 3: return "blob";
|
|
43
|
+
case 4: return "tag";
|
|
44
|
+
default: throw new Error(`Unknown object type number: ${n}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { IDX_V2_FANOUT_SIZE, IDX_V2_SIGNATURE, PACK_SIGNATURE, numberToObjectType, objectTypeToNumber };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/pack/crc32.ts
|
|
2
|
+
/**
|
|
3
|
+
* CRC32 计算工具
|
|
4
|
+
*/
|
|
5
|
+
/** CRC32 查找表缓存 */
|
|
6
|
+
let crc32Table = null;
|
|
7
|
+
/**
|
|
8
|
+
* 计算数据的 CRC32 校验和
|
|
9
|
+
*
|
|
10
|
+
* 使用标准 CRC32 算法(与 Git 兼容)。
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const crc = crc32Value(Buffer.from("hello"));
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function crc32Value(data) {
|
|
18
|
+
const table = getCRC32Table();
|
|
19
|
+
let crc = 4294967295;
|
|
20
|
+
for (let i = 0; i < data.length; i++) crc = crc >>> 8 ^ table[(crc ^ data[i]) & 255];
|
|
21
|
+
return (crc ^ 4294967295) >>> 0;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 获取或生成 CRC32 查找表
|
|
25
|
+
*/
|
|
26
|
+
function getCRC32Table() {
|
|
27
|
+
if (crc32Table) return crc32Table;
|
|
28
|
+
crc32Table = /* @__PURE__ */ new Uint32Array(256);
|
|
29
|
+
for (let i = 0; i < 256; i++) {
|
|
30
|
+
let crc = i;
|
|
31
|
+
for (let j = 0; j < 8; j++) if (crc & 1) crc = crc >>> 1 ^ 3988292384;
|
|
32
|
+
else crc = crc >>> 1;
|
|
33
|
+
crc32Table[i] = crc;
|
|
34
|
+
}
|
|
35
|
+
return crc32Table;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { crc32Value };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/pack/delta-apply.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Delta 应用(解码)
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 将 delta 数据应用到 base object,生成目标对象
|
|
7
|
+
*
|
|
8
|
+
* @param base - base object 的原始数据
|
|
9
|
+
* @param delta - delta 数据
|
|
10
|
+
* @returns 目标对象的原始数据
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const base = Buffer.from("hello world");
|
|
15
|
+
* const delta = createDelta(base, Buffer.from("hello git"));
|
|
16
|
+
* const result = applyDelta(base, delta);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
declare function applyDelta(base: Buffer, delta: Buffer): Buffer;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { applyDelta };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { DeltaError } from "../core/errors.mjs";
|
|
2
|
+
import { decodeVarint } from "./varint.mjs";
|
|
3
|
+
//#region src/pack/delta-apply.ts
|
|
4
|
+
/**
|
|
5
|
+
* Delta 应用(解码)
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 将 delta 数据应用到 base object,生成目标对象
|
|
9
|
+
*
|
|
10
|
+
* @param base - base object 的原始数据
|
|
11
|
+
* @param delta - delta 数据
|
|
12
|
+
* @returns 目标对象的原始数据
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const base = Buffer.from("hello world");
|
|
17
|
+
* const delta = createDelta(base, Buffer.from("hello git"));
|
|
18
|
+
* const result = applyDelta(base, delta);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function applyDelta(base, delta) {
|
|
22
|
+
let offset = 0;
|
|
23
|
+
const [_srcSize, srcBytes] = decodeVarint(delta, offset);
|
|
24
|
+
offset += srcBytes;
|
|
25
|
+
const [destSize, destBytes] = decodeVarint(delta, offset);
|
|
26
|
+
offset += destBytes;
|
|
27
|
+
const result = Buffer.alloc(destSize);
|
|
28
|
+
let destOffset = 0;
|
|
29
|
+
while (offset < delta.length) {
|
|
30
|
+
const cmd = delta[offset];
|
|
31
|
+
offset++;
|
|
32
|
+
if (cmd & 128) {
|
|
33
|
+
const copy = decodeCopyInstruction(delta, offset, cmd);
|
|
34
|
+
offset += copy.bytesRead;
|
|
35
|
+
if (copy.offset + copy.size > base.length) throw new DeltaError(`Copy out of bounds: offset=${copy.offset}, size=${copy.size}, base.length=${base.length}`);
|
|
36
|
+
base.copy(result, destOffset, copy.offset, copy.offset + copy.size);
|
|
37
|
+
destOffset += copy.size;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (cmd > 0) {
|
|
41
|
+
if (offset + cmd > delta.length) throw new DeltaError("Insert out of bounds");
|
|
42
|
+
delta.copy(result, destOffset, offset, offset + cmd);
|
|
43
|
+
destOffset += cmd;
|
|
44
|
+
offset += cmd;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
throw new DeltaError("Unexpected delta command: 0");
|
|
48
|
+
}
|
|
49
|
+
if (destOffset !== destSize) throw new DeltaError(`Delta size mismatch: expected ${destSize}, got ${destOffset}`);
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
function decodeCopyInstruction(delta, offset, cmd) {
|
|
53
|
+
const start = offset;
|
|
54
|
+
let copyOffset = 0;
|
|
55
|
+
let copySize = 0;
|
|
56
|
+
if (cmd & 1) copyOffset = delta[offset++];
|
|
57
|
+
if (cmd & 2) copyOffset |= delta[offset++] << 8;
|
|
58
|
+
if (cmd & 4) copyOffset |= delta[offset++] << 16;
|
|
59
|
+
if (cmd & 8) copyOffset |= delta[offset++] << 24;
|
|
60
|
+
if (cmd & 16) copySize = delta[offset++];
|
|
61
|
+
if (cmd & 32) copySize |= delta[offset++] << 8;
|
|
62
|
+
if (cmd & 64) copySize |= delta[offset++] << 16;
|
|
63
|
+
if (copySize === 0) copySize = 65536;
|
|
64
|
+
return {
|
|
65
|
+
offset: copyOffset,
|
|
66
|
+
size: copySize,
|
|
67
|
+
bytesRead: offset - start
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { applyDelta };
|