gitx.do 0.1.1 → 0.1.2

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.
Files changed (356) hide show
  1. package/README.md +40 -353
  2. package/dist/do/logger.d.ts +50 -0
  3. package/dist/do/logger.d.ts.map +1 -0
  4. package/dist/do/logger.js +122 -0
  5. package/dist/do/logger.js.map +1 -0
  6. package/dist/{durable-object → do}/schema.d.ts +3 -3
  7. package/dist/do/schema.d.ts.map +1 -0
  8. package/dist/{durable-object → do}/schema.js +4 -3
  9. package/dist/do/schema.js.map +1 -0
  10. package/dist/do/types.d.ts +267 -0
  11. package/dist/do/types.d.ts.map +1 -0
  12. package/dist/do/types.js +62 -0
  13. package/dist/do/types.js.map +1 -0
  14. package/dist/index.d.ts +14 -469
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +31 -483
  17. package/dist/index.js.map +1 -1
  18. package/package.json +13 -21
  19. package/dist/cli/commands/add.d.ts +0 -176
  20. package/dist/cli/commands/add.d.ts.map +0 -1
  21. package/dist/cli/commands/add.js +0 -979
  22. package/dist/cli/commands/add.js.map +0 -1
  23. package/dist/cli/commands/blame.d.ts +0 -259
  24. package/dist/cli/commands/blame.d.ts.map +0 -1
  25. package/dist/cli/commands/blame.js +0 -609
  26. package/dist/cli/commands/blame.js.map +0 -1
  27. package/dist/cli/commands/branch.d.ts +0 -249
  28. package/dist/cli/commands/branch.d.ts.map +0 -1
  29. package/dist/cli/commands/branch.js +0 -693
  30. package/dist/cli/commands/branch.js.map +0 -1
  31. package/dist/cli/commands/checkout.d.ts +0 -73
  32. package/dist/cli/commands/checkout.d.ts.map +0 -1
  33. package/dist/cli/commands/checkout.js +0 -725
  34. package/dist/cli/commands/checkout.js.map +0 -1
  35. package/dist/cli/commands/commit.d.ts +0 -182
  36. package/dist/cli/commands/commit.d.ts.map +0 -1
  37. package/dist/cli/commands/commit.js +0 -457
  38. package/dist/cli/commands/commit.js.map +0 -1
  39. package/dist/cli/commands/diff.d.ts +0 -464
  40. package/dist/cli/commands/diff.d.ts.map +0 -1
  41. package/dist/cli/commands/diff.js +0 -959
  42. package/dist/cli/commands/diff.js.map +0 -1
  43. package/dist/cli/commands/log.d.ts +0 -239
  44. package/dist/cli/commands/log.d.ts.map +0 -1
  45. package/dist/cli/commands/log.js +0 -535
  46. package/dist/cli/commands/log.js.map +0 -1
  47. package/dist/cli/commands/merge.d.ts +0 -106
  48. package/dist/cli/commands/merge.d.ts.map +0 -1
  49. package/dist/cli/commands/merge.js +0 -852
  50. package/dist/cli/commands/merge.js.map +0 -1
  51. package/dist/cli/commands/review.d.ts +0 -457
  52. package/dist/cli/commands/review.d.ts.map +0 -1
  53. package/dist/cli/commands/review.js +0 -558
  54. package/dist/cli/commands/review.js.map +0 -1
  55. package/dist/cli/commands/stash.d.ts +0 -157
  56. package/dist/cli/commands/stash.d.ts.map +0 -1
  57. package/dist/cli/commands/stash.js +0 -655
  58. package/dist/cli/commands/stash.js.map +0 -1
  59. package/dist/cli/commands/status.d.ts +0 -269
  60. package/dist/cli/commands/status.d.ts.map +0 -1
  61. package/dist/cli/commands/status.js +0 -492
  62. package/dist/cli/commands/status.js.map +0 -1
  63. package/dist/cli/commands/web.d.ts +0 -199
  64. package/dist/cli/commands/web.d.ts.map +0 -1
  65. package/dist/cli/commands/web.js +0 -697
  66. package/dist/cli/commands/web.js.map +0 -1
  67. package/dist/cli/fs-adapter.d.ts +0 -656
  68. package/dist/cli/fs-adapter.d.ts.map +0 -1
  69. package/dist/cli/fs-adapter.js +0 -1177
  70. package/dist/cli/fs-adapter.js.map +0 -1
  71. package/dist/cli/fsx-cli-adapter.d.ts +0 -359
  72. package/dist/cli/fsx-cli-adapter.d.ts.map +0 -1
  73. package/dist/cli/fsx-cli-adapter.js +0 -619
  74. package/dist/cli/fsx-cli-adapter.js.map +0 -1
  75. package/dist/cli/index.d.ts +0 -387
  76. package/dist/cli/index.d.ts.map +0 -1
  77. package/dist/cli/index.js +0 -579
  78. package/dist/cli/index.js.map +0 -1
  79. package/dist/cli/ui/components/DiffView.d.ts +0 -12
  80. package/dist/cli/ui/components/DiffView.d.ts.map +0 -1
  81. package/dist/cli/ui/components/DiffView.js +0 -11
  82. package/dist/cli/ui/components/DiffView.js.map +0 -1
  83. package/dist/cli/ui/components/ErrorDisplay.d.ts +0 -10
  84. package/dist/cli/ui/components/ErrorDisplay.d.ts.map +0 -1
  85. package/dist/cli/ui/components/ErrorDisplay.js +0 -11
  86. package/dist/cli/ui/components/ErrorDisplay.js.map +0 -1
  87. package/dist/cli/ui/components/FuzzySearch.d.ts +0 -15
  88. package/dist/cli/ui/components/FuzzySearch.d.ts.map +0 -1
  89. package/dist/cli/ui/components/FuzzySearch.js +0 -12
  90. package/dist/cli/ui/components/FuzzySearch.js.map +0 -1
  91. package/dist/cli/ui/components/LoadingSpinner.d.ts +0 -10
  92. package/dist/cli/ui/components/LoadingSpinner.d.ts.map +0 -1
  93. package/dist/cli/ui/components/LoadingSpinner.js +0 -10
  94. package/dist/cli/ui/components/LoadingSpinner.js.map +0 -1
  95. package/dist/cli/ui/components/NavigationList.d.ts +0 -14
  96. package/dist/cli/ui/components/NavigationList.d.ts.map +0 -1
  97. package/dist/cli/ui/components/NavigationList.js +0 -11
  98. package/dist/cli/ui/components/NavigationList.js.map +0 -1
  99. package/dist/cli/ui/components/ScrollableContent.d.ts +0 -13
  100. package/dist/cli/ui/components/ScrollableContent.d.ts.map +0 -1
  101. package/dist/cli/ui/components/ScrollableContent.js +0 -11
  102. package/dist/cli/ui/components/ScrollableContent.js.map +0 -1
  103. package/dist/cli/ui/components/index.d.ts +0 -7
  104. package/dist/cli/ui/components/index.d.ts.map +0 -1
  105. package/dist/cli/ui/components/index.js +0 -9
  106. package/dist/cli/ui/components/index.js.map +0 -1
  107. package/dist/cli/ui/terminal-ui.d.ts +0 -85
  108. package/dist/cli/ui/terminal-ui.d.ts.map +0 -1
  109. package/dist/cli/ui/terminal-ui.js +0 -121
  110. package/dist/cli/ui/terminal-ui.js.map +0 -1
  111. package/dist/do/BashModule.d.ts +0 -871
  112. package/dist/do/BashModule.d.ts.map +0 -1
  113. package/dist/do/BashModule.js +0 -1143
  114. package/dist/do/BashModule.js.map +0 -1
  115. package/dist/do/FsModule.d.ts +0 -612
  116. package/dist/do/FsModule.d.ts.map +0 -1
  117. package/dist/do/FsModule.js +0 -1120
  118. package/dist/do/FsModule.js.map +0 -1
  119. package/dist/do/GitModule.d.ts +0 -635
  120. package/dist/do/GitModule.d.ts.map +0 -1
  121. package/dist/do/GitModule.js +0 -784
  122. package/dist/do/GitModule.js.map +0 -1
  123. package/dist/do/GitRepoDO.d.ts +0 -281
  124. package/dist/do/GitRepoDO.d.ts.map +0 -1
  125. package/dist/do/GitRepoDO.js +0 -479
  126. package/dist/do/GitRepoDO.js.map +0 -1
  127. package/dist/do/bash-ast.d.ts +0 -246
  128. package/dist/do/bash-ast.d.ts.map +0 -1
  129. package/dist/do/bash-ast.js +0 -888
  130. package/dist/do/bash-ast.js.map +0 -1
  131. package/dist/do/container-executor.d.ts +0 -491
  132. package/dist/do/container-executor.d.ts.map +0 -1
  133. package/dist/do/container-executor.js +0 -731
  134. package/dist/do/container-executor.js.map +0 -1
  135. package/dist/do/index.d.ts +0 -53
  136. package/dist/do/index.d.ts.map +0 -1
  137. package/dist/do/index.js +0 -91
  138. package/dist/do/index.js.map +0 -1
  139. package/dist/do/tiered-storage.d.ts +0 -403
  140. package/dist/do/tiered-storage.d.ts.map +0 -1
  141. package/dist/do/tiered-storage.js +0 -689
  142. package/dist/do/tiered-storage.js.map +0 -1
  143. package/dist/do/withBash.d.ts +0 -231
  144. package/dist/do/withBash.d.ts.map +0 -1
  145. package/dist/do/withBash.js +0 -244
  146. package/dist/do/withBash.js.map +0 -1
  147. package/dist/do/withFs.d.ts +0 -237
  148. package/dist/do/withFs.d.ts.map +0 -1
  149. package/dist/do/withFs.js +0 -387
  150. package/dist/do/withFs.js.map +0 -1
  151. package/dist/do/withGit.d.ts +0 -180
  152. package/dist/do/withGit.d.ts.map +0 -1
  153. package/dist/do/withGit.js +0 -271
  154. package/dist/do/withGit.js.map +0 -1
  155. package/dist/durable-object/object-store.d.ts +0 -633
  156. package/dist/durable-object/object-store.d.ts.map +0 -1
  157. package/dist/durable-object/object-store.js +0 -1164
  158. package/dist/durable-object/object-store.js.map +0 -1
  159. package/dist/durable-object/schema.d.ts.map +0 -1
  160. package/dist/durable-object/schema.js.map +0 -1
  161. package/dist/durable-object/wal.d.ts +0 -416
  162. package/dist/durable-object/wal.d.ts.map +0 -1
  163. package/dist/durable-object/wal.js +0 -445
  164. package/dist/durable-object/wal.js.map +0 -1
  165. package/dist/mcp/adapter.d.ts +0 -772
  166. package/dist/mcp/adapter.d.ts.map +0 -1
  167. package/dist/mcp/adapter.js +0 -895
  168. package/dist/mcp/adapter.js.map +0 -1
  169. package/dist/mcp/sandbox/miniflare-evaluator.d.ts +0 -22
  170. package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +0 -1
  171. package/dist/mcp/sandbox/miniflare-evaluator.js +0 -140
  172. package/dist/mcp/sandbox/miniflare-evaluator.js.map +0 -1
  173. package/dist/mcp/sandbox/object-store-proxy.d.ts +0 -32
  174. package/dist/mcp/sandbox/object-store-proxy.d.ts.map +0 -1
  175. package/dist/mcp/sandbox/object-store-proxy.js +0 -30
  176. package/dist/mcp/sandbox/object-store-proxy.js.map +0 -1
  177. package/dist/mcp/sandbox/template.d.ts +0 -17
  178. package/dist/mcp/sandbox/template.d.ts.map +0 -1
  179. package/dist/mcp/sandbox/template.js +0 -71
  180. package/dist/mcp/sandbox/template.js.map +0 -1
  181. package/dist/mcp/sandbox.d.ts +0 -764
  182. package/dist/mcp/sandbox.d.ts.map +0 -1
  183. package/dist/mcp/sandbox.js +0 -1362
  184. package/dist/mcp/sandbox.js.map +0 -1
  185. package/dist/mcp/sdk-adapter.d.ts +0 -835
  186. package/dist/mcp/sdk-adapter.d.ts.map +0 -1
  187. package/dist/mcp/sdk-adapter.js +0 -974
  188. package/dist/mcp/sdk-adapter.js.map +0 -1
  189. package/dist/mcp/tools/do.d.ts +0 -32
  190. package/dist/mcp/tools/do.d.ts.map +0 -1
  191. package/dist/mcp/tools/do.js +0 -117
  192. package/dist/mcp/tools/do.js.map +0 -1
  193. package/dist/mcp/tools.d.ts +0 -548
  194. package/dist/mcp/tools.d.ts.map +0 -1
  195. package/dist/mcp/tools.js +0 -3170
  196. package/dist/mcp/tools.js.map +0 -1
  197. package/dist/ops/blame.d.ts +0 -551
  198. package/dist/ops/blame.d.ts.map +0 -1
  199. package/dist/ops/blame.js +0 -1037
  200. package/dist/ops/blame.js.map +0 -1
  201. package/dist/ops/branch.d.ts +0 -766
  202. package/dist/ops/branch.d.ts.map +0 -1
  203. package/dist/ops/branch.js +0 -950
  204. package/dist/ops/branch.js.map +0 -1
  205. package/dist/ops/commit-traversal.d.ts +0 -349
  206. package/dist/ops/commit-traversal.d.ts.map +0 -1
  207. package/dist/ops/commit-traversal.js +0 -821
  208. package/dist/ops/commit-traversal.js.map +0 -1
  209. package/dist/ops/commit.d.ts +0 -555
  210. package/dist/ops/commit.d.ts.map +0 -1
  211. package/dist/ops/commit.js +0 -826
  212. package/dist/ops/commit.js.map +0 -1
  213. package/dist/ops/merge-base.d.ts +0 -397
  214. package/dist/ops/merge-base.d.ts.map +0 -1
  215. package/dist/ops/merge-base.js +0 -691
  216. package/dist/ops/merge-base.js.map +0 -1
  217. package/dist/ops/merge.d.ts +0 -855
  218. package/dist/ops/merge.d.ts.map +0 -1
  219. package/dist/ops/merge.js +0 -1551
  220. package/dist/ops/merge.js.map +0 -1
  221. package/dist/ops/tag.d.ts +0 -247
  222. package/dist/ops/tag.d.ts.map +0 -1
  223. package/dist/ops/tag.js +0 -649
  224. package/dist/ops/tag.js.map +0 -1
  225. package/dist/ops/tree-builder.d.ts +0 -178
  226. package/dist/ops/tree-builder.d.ts.map +0 -1
  227. package/dist/ops/tree-builder.js +0 -271
  228. package/dist/ops/tree-builder.js.map +0 -1
  229. package/dist/ops/tree-diff.d.ts +0 -291
  230. package/dist/ops/tree-diff.d.ts.map +0 -1
  231. package/dist/ops/tree-diff.js +0 -705
  232. package/dist/ops/tree-diff.js.map +0 -1
  233. package/dist/pack/delta.d.ts +0 -248
  234. package/dist/pack/delta.d.ts.map +0 -1
  235. package/dist/pack/delta.js +0 -740
  236. package/dist/pack/delta.js.map +0 -1
  237. package/dist/pack/format.d.ts +0 -446
  238. package/dist/pack/format.d.ts.map +0 -1
  239. package/dist/pack/format.js +0 -572
  240. package/dist/pack/format.js.map +0 -1
  241. package/dist/pack/full-generation.d.ts +0 -612
  242. package/dist/pack/full-generation.d.ts.map +0 -1
  243. package/dist/pack/full-generation.js +0 -1378
  244. package/dist/pack/full-generation.js.map +0 -1
  245. package/dist/pack/generation.d.ts +0 -441
  246. package/dist/pack/generation.d.ts.map +0 -1
  247. package/dist/pack/generation.js +0 -707
  248. package/dist/pack/generation.js.map +0 -1
  249. package/dist/pack/index.d.ts +0 -502
  250. package/dist/pack/index.d.ts.map +0 -1
  251. package/dist/pack/index.js +0 -833
  252. package/dist/pack/index.js.map +0 -1
  253. package/dist/refs/branch.d.ts +0 -683
  254. package/dist/refs/branch.d.ts.map +0 -1
  255. package/dist/refs/branch.js +0 -881
  256. package/dist/refs/branch.js.map +0 -1
  257. package/dist/refs/storage.d.ts +0 -833
  258. package/dist/refs/storage.d.ts.map +0 -1
  259. package/dist/refs/storage.js +0 -1023
  260. package/dist/refs/storage.js.map +0 -1
  261. package/dist/refs/tag.d.ts +0 -860
  262. package/dist/refs/tag.d.ts.map +0 -1
  263. package/dist/refs/tag.js +0 -996
  264. package/dist/refs/tag.js.map +0 -1
  265. package/dist/storage/backend.d.ts +0 -425
  266. package/dist/storage/backend.d.ts.map +0 -1
  267. package/dist/storage/backend.js +0 -41
  268. package/dist/storage/backend.js.map +0 -1
  269. package/dist/storage/fsx-adapter.d.ts +0 -204
  270. package/dist/storage/fsx-adapter.d.ts.map +0 -1
  271. package/dist/storage/fsx-adapter.js +0 -518
  272. package/dist/storage/fsx-adapter.js.map +0 -1
  273. package/dist/storage/lru-cache.d.ts +0 -691
  274. package/dist/storage/lru-cache.d.ts.map +0 -1
  275. package/dist/storage/lru-cache.js +0 -813
  276. package/dist/storage/lru-cache.js.map +0 -1
  277. package/dist/storage/object-index.d.ts +0 -585
  278. package/dist/storage/object-index.d.ts.map +0 -1
  279. package/dist/storage/object-index.js +0 -532
  280. package/dist/storage/object-index.js.map +0 -1
  281. package/dist/storage/r2-pack.d.ts +0 -1257
  282. package/dist/storage/r2-pack.d.ts.map +0 -1
  283. package/dist/storage/r2-pack.js +0 -1773
  284. package/dist/storage/r2-pack.js.map +0 -1
  285. package/dist/tiered/cdc-pipeline.d.ts +0 -1888
  286. package/dist/tiered/cdc-pipeline.d.ts.map +0 -1
  287. package/dist/tiered/cdc-pipeline.js +0 -1880
  288. package/dist/tiered/cdc-pipeline.js.map +0 -1
  289. package/dist/tiered/migration.d.ts +0 -1104
  290. package/dist/tiered/migration.d.ts.map +0 -1
  291. package/dist/tiered/migration.js +0 -1217
  292. package/dist/tiered/migration.js.map +0 -1
  293. package/dist/tiered/parquet-writer.d.ts +0 -1145
  294. package/dist/tiered/parquet-writer.d.ts.map +0 -1
  295. package/dist/tiered/parquet-writer.js +0 -1183
  296. package/dist/tiered/parquet-writer.js.map +0 -1
  297. package/dist/tiered/read-path.d.ts +0 -835
  298. package/dist/tiered/read-path.d.ts.map +0 -1
  299. package/dist/tiered/read-path.js +0 -487
  300. package/dist/tiered/read-path.js.map +0 -1
  301. package/dist/types/capability.d.ts +0 -1385
  302. package/dist/types/capability.d.ts.map +0 -1
  303. package/dist/types/capability.js +0 -36
  304. package/dist/types/capability.js.map +0 -1
  305. package/dist/types/index.d.ts +0 -13
  306. package/dist/types/index.d.ts.map +0 -1
  307. package/dist/types/index.js +0 -18
  308. package/dist/types/index.js.map +0 -1
  309. package/dist/types/interfaces.d.ts +0 -673
  310. package/dist/types/interfaces.d.ts.map +0 -1
  311. package/dist/types/interfaces.js +0 -26
  312. package/dist/types/interfaces.js.map +0 -1
  313. package/dist/types/objects.d.ts +0 -692
  314. package/dist/types/objects.d.ts.map +0 -1
  315. package/dist/types/objects.js +0 -837
  316. package/dist/types/objects.js.map +0 -1
  317. package/dist/types/storage.d.ts +0 -603
  318. package/dist/types/storage.d.ts.map +0 -1
  319. package/dist/types/storage.js +0 -191
  320. package/dist/types/storage.js.map +0 -1
  321. package/dist/types/worker-loader.d.ts +0 -60
  322. package/dist/types/worker-loader.d.ts.map +0 -1
  323. package/dist/types/worker-loader.js +0 -62
  324. package/dist/types/worker-loader.js.map +0 -1
  325. package/dist/utils/hash.d.ts +0 -198
  326. package/dist/utils/hash.d.ts.map +0 -1
  327. package/dist/utils/hash.js +0 -272
  328. package/dist/utils/hash.js.map +0 -1
  329. package/dist/utils/sha1.d.ts +0 -325
  330. package/dist/utils/sha1.d.ts.map +0 -1
  331. package/dist/utils/sha1.js +0 -635
  332. package/dist/utils/sha1.js.map +0 -1
  333. package/dist/wire/capabilities.d.ts +0 -1044
  334. package/dist/wire/capabilities.d.ts.map +0 -1
  335. package/dist/wire/capabilities.js +0 -941
  336. package/dist/wire/capabilities.js.map +0 -1
  337. package/dist/wire/path-security.d.ts +0 -157
  338. package/dist/wire/path-security.d.ts.map +0 -1
  339. package/dist/wire/path-security.js +0 -307
  340. package/dist/wire/path-security.js.map +0 -1
  341. package/dist/wire/pkt-line.d.ts +0 -345
  342. package/dist/wire/pkt-line.d.ts.map +0 -1
  343. package/dist/wire/pkt-line.js +0 -381
  344. package/dist/wire/pkt-line.js.map +0 -1
  345. package/dist/wire/receive-pack.d.ts +0 -1059
  346. package/dist/wire/receive-pack.d.ts.map +0 -1
  347. package/dist/wire/receive-pack.js +0 -1414
  348. package/dist/wire/receive-pack.js.map +0 -1
  349. package/dist/wire/smart-http.d.ts +0 -799
  350. package/dist/wire/smart-http.d.ts.map +0 -1
  351. package/dist/wire/smart-http.js +0 -945
  352. package/dist/wire/smart-http.js.map +0 -1
  353. package/dist/wire/upload-pack.d.ts +0 -727
  354. package/dist/wire/upload-pack.d.ts.map +0 -1
  355. package/dist/wire/upload-pack.js +0 -1141
  356. package/dist/wire/upload-pack.js.map +0 -1
@@ -1,740 +0,0 @@
1
- /**
2
- * @fileoverview Git Packfile Delta Encoding/Decoding
3
- *
4
- * This module implements Git's delta compression algorithm, which is used in packfiles
5
- * to efficiently store objects by encoding only the differences between similar objects.
6
- *
7
- * ## Delta Format Overview
8
- *
9
- * A delta consists of:
10
- * 1. **Source size** - Variable-length integer specifying the base object size
11
- * 2. **Target size** - Variable-length integer specifying the result size
12
- * 3. **Instructions** - Sequence of copy or insert commands
13
- *
14
- * ## Instruction Types
15
- *
16
- * ### Copy Instruction (MSB = 1)
17
- * Copies a range of bytes from the source (base) object.
18
- *
19
- * | Bit | Meaning |
20
- * |--------|-------------------------------------------|
21
- * | 7 | Always 1 (copy marker) |
22
- * | 6-4 | Which size bytes follow (bit mask) |
23
- * | 3-0 | Which offset bytes follow (bit mask) |
24
- *
25
- * Following bytes encode offset (up to 4 bytes) and size (up to 3 bytes).
26
- * If size is 0 after decoding, it means 0x10000 (65536).
27
- *
28
- * ### Insert Instruction (MSB = 0)
29
- * Inserts literal bytes directly into the output.
30
- *
31
- * | Bit | Meaning |
32
- * |--------|-------------------------------------------|
33
- * | 7 | Always 0 (insert marker) |
34
- * | 6-0 | Number of bytes to insert (1-127) |
35
- *
36
- * The instruction byte is followed by that many literal bytes.
37
- *
38
- * ## Performance Optimizations
39
- *
40
- * This implementation includes several performance optimizations:
41
- * - **Rabin fingerprint rolling hash**: O(1) hash updates when sliding the window
42
- * - **Typed arrays for index**: Uses Uint32Array for memory efficiency
43
- * - **Chunked processing**: Memory-efficient processing for large files
44
- * - **Optimized match extension**: SIMD-friendly byte comparison
45
- *
46
- * @module pack/delta
47
- * @see {@link https://git-scm.com/docs/pack-format} Git Pack Format Documentation
48
- *
49
- * @example
50
- * // Apply a delta to reconstruct an object
51
- * import { applyDelta } from './delta';
52
- *
53
- * const baseObject = getBaseObject(); // Uint8Array
54
- * const deltaData = getDeltaData(); // Uint8Array from packfile
55
- * const targetObject = applyDelta(baseObject, deltaData);
56
- *
57
- * @example
58
- * // Create a delta between two objects
59
- * import { createDelta } from './delta';
60
- *
61
- * const oldVersion = new TextEncoder().encode('Hello, World!');
62
- * const newVersion = new TextEncoder().encode('Hello, Universe!');
63
- * const delta = createDelta(oldVersion, newVersion);
64
- */
65
- /**
66
- * Marker byte for copy instructions (MSB set).
67
- * When the MSB is set, the instruction copies bytes from the base object.
68
- *
69
- * @constant {number}
70
- */
71
- export const COPY_INSTRUCTION = 0x80;
72
- /**
73
- * Marker byte for insert instructions (MSB clear).
74
- * When the MSB is clear, the lower 7 bits indicate how many literal bytes follow.
75
- *
76
- * @constant {number}
77
- */
78
- export const INSERT_INSTRUCTION = 0x00;
79
- // =============================================================================
80
- // Rabin Fingerprint Rolling Hash Constants and Implementation
81
- // =============================================================================
82
- /**
83
- * Window size for rolling hash matching.
84
- * 4 bytes is a good balance between collision rate and match granularity.
85
- *
86
- * @constant {number}
87
- */
88
- const WINDOW_SIZE = 4;
89
- /**
90
- * Rabin fingerprint polynomial base.
91
- * Using a prime number provides good hash distribution.
92
- *
93
- * @constant {number}
94
- */
95
- const RABIN_BASE = 257;
96
- /**
97
- * Modulus for Rabin fingerprint to keep values in 32-bit range.
98
- * Using a prime close to 2^31 for good distribution.
99
- *
100
- * @constant {number}
101
- */
102
- const RABIN_MOD = 0x7fffffff; // 2^31 - 1, a Mersenne prime
103
- /**
104
- * Pre-computed power of RABIN_BASE^WINDOW_SIZE mod RABIN_MOD.
105
- * Used for efficiently removing the contribution of the oldest byte.
106
- *
107
- * @constant {number}
108
- */
109
- const RABIN_POW = (() => {
110
- let pow = 1;
111
- for (let i = 0; i < WINDOW_SIZE; i++) {
112
- pow = (pow * RABIN_BASE) % RABIN_MOD;
113
- }
114
- return pow;
115
- })();
116
- /**
117
- * Minimum match length to be worth a copy instruction.
118
- * Smaller matches cost more than they save due to instruction overhead.
119
- *
120
- * @constant {number}
121
- */
122
- const MIN_COPY_SIZE = 4;
123
- /**
124
- * Chunk size for processing large files.
125
- * 64KB chunks balance memory usage and cache efficiency.
126
- *
127
- * @constant {number}
128
- */
129
- const CHUNK_SIZE = 64 * 1024;
130
- /**
131
- * Maximum entries per hash bucket to prevent pathological cases.
132
- * Limits memory usage when many positions hash to the same value.
133
- *
134
- * @constant {number}
135
- */
136
- const MAX_BUCKET_SIZE = 64;
137
- /**
138
- * Computes Rabin fingerprint hash for initial window.
139
- *
140
- * @description Computes the hash value for the first WINDOW_SIZE bytes.
141
- * Subsequent positions use rolling updates for O(1) computation.
142
- *
143
- * @param {Uint8Array} data - The data buffer
144
- * @param {number} offset - Starting offset
145
- * @returns {number} 32-bit hash value
146
- * @internal
147
- */
148
- function rabinHash(data, offset) {
149
- let hash = 0;
150
- for (let i = 0; i < WINDOW_SIZE; i++) {
151
- hash = (hash * RABIN_BASE + data[offset + i]) % RABIN_MOD;
152
- }
153
- return hash;
154
- }
155
- /**
156
- * Updates Rabin fingerprint hash by rolling the window one byte forward.
157
- *
158
- * @description This is the key optimization: O(1) hash update instead of O(WINDOW_SIZE).
159
- * Removes contribution of outgoing byte and adds incoming byte.
160
- *
161
- * Formula: hash' = (hash * BASE - outgoing * BASE^WINDOW + incoming) mod MOD
162
- *
163
- * @param {number} hash - Current hash value
164
- * @param {number} outgoing - Byte leaving the window
165
- * @param {number} incoming - Byte entering the window
166
- * @returns {number} Updated hash value
167
- * @internal
168
- */
169
- function rabinRoll(hash, outgoing, incoming) {
170
- // Remove outgoing byte's contribution and add incoming byte
171
- // hash = hash * BASE - outgoing * BASE^WINDOW + incoming
172
- let newHash = ((hash * RABIN_BASE) % RABIN_MOD - (outgoing * RABIN_POW) % RABIN_MOD + incoming) % RABIN_MOD;
173
- // Ensure positive result
174
- if (newHash < 0)
175
- newHash += RABIN_MOD;
176
- return newHash;
177
- }
178
- /**
179
- * Builds an optimized hash index from the base object using Rabin fingerprints.
180
- *
181
- * @description Creates a memory-efficient index for finding matching byte sequences.
182
- * Uses typed arrays to minimize GC pressure and memory fragmentation.
183
- *
184
- * **Memory optimization:**
185
- * - Uses power-of-2 bucket count for fast modulo (bitwise AND)
186
- * - Limits bucket size to prevent pathological cases
187
- * - Uses Uint32Array/Uint16Array instead of object arrays
188
- *
189
- * @param {Uint8Array} base - The base object to index
190
- * @returns {HashIndex} The built index
191
- * @internal
192
- */
193
- function buildHashIndex(base) {
194
- // Determine optimal bucket count (power of 2, at least 256)
195
- // Aim for average load factor of ~4 entries per bucket
196
- const targetBuckets = Math.max(256, Math.ceil((base.length - WINDOW_SIZE + 1) / 4));
197
- const bucketCount = 1 << Math.ceil(Math.log2(targetBuckets));
198
- const bucketMask = bucketCount - 1;
199
- // Allocate typed arrays
200
- const offsets = new Uint32Array(bucketCount * MAX_BUCKET_SIZE);
201
- const counts = new Uint16Array(bucketCount);
202
- if (base.length < WINDOW_SIZE) {
203
- return { bucketCount, bucketMask, offsets, counts };
204
- }
205
- // Use rolling hash to build index
206
- let hash = rabinHash(base, 0);
207
- addToIndex(0);
208
- for (let i = 1; i <= base.length - WINDOW_SIZE; i++) {
209
- // Roll the hash forward (O(1) operation)
210
- hash = rabinRoll(hash, base[i - 1], base[i + WINDOW_SIZE - 1]);
211
- addToIndex(i);
212
- }
213
- return { bucketCount, bucketMask, offsets, counts };
214
- function addToIndex(offset) {
215
- const bucket = hash & bucketMask;
216
- const count = counts[bucket];
217
- if (count < MAX_BUCKET_SIZE) {
218
- offsets[bucket * MAX_BUCKET_SIZE + count] = offset;
219
- counts[bucket] = count + 1;
220
- }
221
- }
222
- }
223
- /**
224
- * Looks up potential match positions from the hash index.
225
- *
226
- * @param {HashIndex} index - The hash index
227
- * @param {number} hash - The hash to look up
228
- * @returns {Generator<number>} Yields matching offsets
229
- * @internal
230
- */
231
- function* lookupIndex(index, hash) {
232
- const bucket = hash & index.bucketMask;
233
- const count = index.counts[bucket];
234
- const baseOffset = bucket * MAX_BUCKET_SIZE;
235
- for (let i = 0; i < count; i++) {
236
- yield index.offsets[baseOffset + i];
237
- }
238
- }
239
- /**
240
- * Optimized match length calculation using word-at-a-time comparison.
241
- *
242
- * @description Compares bytes in chunks of 4 (as 32-bit integers) where possible,
243
- * falling back to byte-by-byte for the remaining bytes. This is ~4x faster than
244
- * pure byte comparison for long matches.
245
- *
246
- * @param {Uint8Array} a - First array
247
- * @param {number} aOffset - Starting offset in first array
248
- * @param {Uint8Array} b - Second array
249
- * @param {number} bOffset - Starting offset in second array
250
- * @param {number} maxLength - Maximum number of bytes to compare
251
- * @returns {number} Number of matching bytes (0 to maxLength)
252
- * @internal
253
- */
254
- function getMatchLengthOptimized(a, aOffset, b, bOffset, maxLength) {
255
- if (maxLength <= 0)
256
- return 0;
257
- let length = 0;
258
- // Compare 4 bytes at a time using DataView for unaligned access
259
- // This is faster than byte-by-byte for larger matches
260
- const wordCount = (maxLength - length) >>> 2;
261
- if (wordCount > 0) {
262
- const aView = new DataView(a.buffer, a.byteOffset + aOffset, maxLength);
263
- const bView = new DataView(b.buffer, b.byteOffset + bOffset, maxLength);
264
- for (let i = 0; i < wordCount; i++) {
265
- const wordOffset = length;
266
- if (aView.getUint32(wordOffset, true) !== bView.getUint32(wordOffset, true)) {
267
- // Found difference in this word, find exact byte
268
- while (length < maxLength && a[aOffset + length] === b[bOffset + length]) {
269
- length++;
270
- }
271
- return length;
272
- }
273
- length += 4;
274
- }
275
- }
276
- // Compare remaining bytes
277
- while (length < maxLength && a[aOffset + length] === b[bOffset + length]) {
278
- length++;
279
- }
280
- return length;
281
- }
282
- /**
283
- * Parses a variable-length size value from the delta header.
284
- *
285
- * @description Reads the source or target size from a delta's header using
286
- * Git's variable-length integer encoding. Each byte's MSB indicates whether
287
- * more bytes follow, and the lower 7 bits contribute to the value.
288
- *
289
- * **Encoding Details:**
290
- * - Bytes are read sequentially
291
- * - Lower 7 bits of each byte contribute to the result
292
- * - MSB = 1 means more bytes follow
293
- * - MSB = 0 means this is the last byte
294
- * - Maximum of 10 bytes (supports values up to 2^70)
295
- *
296
- * @param {Uint8Array} data - The delta data buffer
297
- * @param {number} offset - Starting byte offset in the buffer
298
- * @returns {DeltaHeaderResult} Object with parsed size and bytes consumed
299
- * @throws {Error} If data ends unexpectedly before size is complete
300
- * @throws {Error} If size encoding exceeds maximum length (corrupted data)
301
- *
302
- * @example
303
- * // Parse source and target sizes from delta
304
- * let offset = 0;
305
- * const source = parseDeltaHeader(delta, offset);
306
- * offset += source.bytesRead;
307
- * const target = parseDeltaHeader(delta, offset);
308
- * offset += target.bytesRead;
309
- *
310
- * console.log(`Base size: ${source.size}, Target size: ${target.size}`);
311
- */
312
- export function parseDeltaHeader(data, offset) {
313
- let size = 0;
314
- let shift = 0;
315
- let bytesRead = 0;
316
- // Maximum bytes for a varint to prevent infinite loops
317
- const MAX_VARINT_BYTES = 10;
318
- while (true) {
319
- if (offset + bytesRead >= data.length) {
320
- throw new Error(`Delta header parsing failed: unexpected end of data at offset ${offset + bytesRead}`);
321
- }
322
- if (bytesRead >= MAX_VARINT_BYTES) {
323
- throw new Error(`Delta header parsing failed: exceeded maximum length of ${MAX_VARINT_BYTES} bytes (possible infinite loop or corrupted data)`);
324
- }
325
- const byte = data[offset + bytesRead];
326
- bytesRead++;
327
- // Add the lower 7 bits to the result
328
- size |= (byte & 0x7f) << shift;
329
- shift += 7;
330
- // If MSB is not set, we're done
331
- if ((byte & 0x80) === 0) {
332
- break;
333
- }
334
- }
335
- return { size, bytesRead };
336
- }
337
- /**
338
- * Encodes a size as a variable-length integer for delta headers.
339
- *
340
- * @description Internal function that encodes sizes using Git's varint format.
341
- * Used when creating delta headers that specify source and target sizes.
342
- *
343
- * @param {number} size - The size value to encode
344
- * @returns {Uint8Array} The encoded bytes
345
- * @internal
346
- */
347
- function encodeDeltaSize(size) {
348
- const bytes = [];
349
- do {
350
- let byte = size & 0x7f;
351
- size >>>= 7;
352
- if (size > 0) {
353
- byte |= 0x80; // Set continuation bit
354
- }
355
- bytes.push(byte);
356
- } while (size > 0);
357
- return new Uint8Array(bytes);
358
- }
359
- /**
360
- * Applies a delta to a base object to produce the target object.
361
- *
362
- * @description Reconstructs the target object by executing the delta's copy and
363
- * insert instructions against the base object. This is the core operation for
364
- * unpacking delta-compressed objects in packfiles.
365
- *
366
- * **Delta Application Process:**
367
- * 1. Parse source (base) size and verify it matches
368
- * 2. Parse target size to allocate result buffer
369
- * 3. Execute instructions sequentially:
370
- * - Copy: copy bytes from base object to result
371
- * - Insert: copy literal bytes from delta to result
372
- * 4. Verify result size matches expected target size
373
- *
374
- * **Error Conditions:**
375
- * - Base object size doesn't match delta's source size
376
- * - Copy instruction references bytes outside base object
377
- * - Instructions would overflow the result buffer
378
- * - Result size doesn't match delta's target size
379
- * - Invalid instruction byte (0x00)
380
- *
381
- * @param {Uint8Array} base - The source/base object to apply delta against
382
- * @param {Uint8Array} delta - The delta data (decompressed from packfile)
383
- * @returns {Uint8Array} The reconstructed target object
384
- * @throws {Error} If base size doesn't match delta's source size
385
- * @throws {Error} If delta contains invalid instructions
386
- * @throws {Error} If copy would read beyond base object bounds
387
- * @throws {Error} If result size doesn't match expected target size
388
- *
389
- * @example
390
- * // Reconstruct an object from base + delta
391
- * const base = await getObject(baseSha);
392
- * const delta = decompressDeltaData(packData, offset);
393
- * const target = applyDelta(base, delta);
394
- *
395
- * @example
396
- * // Error handling
397
- * try {
398
- * const target = applyDelta(base, delta);
399
- * } catch (e) {
400
- * console.error('Delta application failed:', e.message);
401
- * }
402
- */
403
- export function applyDelta(base, delta) {
404
- let offset = 0;
405
- // Parse source size
406
- const sourceHeader = parseDeltaHeader(delta, offset);
407
- offset += sourceHeader.bytesRead;
408
- if (sourceHeader.size !== base.length) {
409
- throw new Error(`Delta source size mismatch: expected ${sourceHeader.size}, got ${base.length}`);
410
- }
411
- // Parse target size
412
- const targetHeader = parseDeltaHeader(delta, offset);
413
- offset += targetHeader.bytesRead;
414
- // Allocate result buffer
415
- const result = new Uint8Array(targetHeader.size);
416
- let resultOffset = 0;
417
- // Process instructions
418
- while (offset < delta.length) {
419
- const cmd = delta[offset++];
420
- if (cmd & COPY_INSTRUCTION) {
421
- // Copy instruction
422
- let copyOffset = 0;
423
- let copySize = 0;
424
- // Read offset bytes (bits 0-3 indicate which bytes are present)
425
- if (cmd & 0x01)
426
- copyOffset |= delta[offset++];
427
- if (cmd & 0x02)
428
- copyOffset |= delta[offset++] << 8;
429
- if (cmd & 0x04)
430
- copyOffset |= delta[offset++] << 16;
431
- if (cmd & 0x08)
432
- copyOffset |= delta[offset++] << 24;
433
- // Read size bytes (bits 4-6 indicate which bytes are present)
434
- if (cmd & 0x10)
435
- copySize |= delta[offset++];
436
- if (cmd & 0x20)
437
- copySize |= delta[offset++] << 8;
438
- if (cmd & 0x40)
439
- copySize |= delta[offset++] << 16;
440
- // Size of 0 means 0x10000 (65536)
441
- if (copySize === 0) {
442
- copySize = 0x10000;
443
- }
444
- // Bounds checking to prevent buffer overflows
445
- if (copyOffset < 0 || copySize < 0) {
446
- throw new Error(`Invalid copy instruction: offset=${copyOffset}, size=${copySize}`);
447
- }
448
- if (copyOffset + copySize > base.length) {
449
- throw new Error(`Copy instruction out of bounds: offset=${copyOffset}, size=${copySize}, base length=${base.length}`);
450
- }
451
- if (resultOffset + copySize > result.length) {
452
- throw new Error(`Copy would overflow result buffer: resultOffset=${resultOffset}, size=${copySize}, result length=${result.length}`);
453
- }
454
- // Copy from base to result
455
- result.set(base.subarray(copyOffset, copyOffset + copySize), resultOffset);
456
- resultOffset += copySize;
457
- }
458
- else if (cmd !== 0) {
459
- // Insert instruction: cmd is the number of bytes to insert
460
- const insertSize = cmd;
461
- result.set(delta.subarray(offset, offset + insertSize), resultOffset);
462
- offset += insertSize;
463
- resultOffset += insertSize;
464
- }
465
- else {
466
- // cmd === 0 is reserved/invalid
467
- throw new Error('Invalid delta instruction: 0x00');
468
- }
469
- }
470
- // Verify we produced the expected size
471
- if (resultOffset !== targetHeader.size) {
472
- throw new Error(`Delta result size mismatch: expected ${targetHeader.size}, got ${resultOffset}`);
473
- }
474
- return result;
475
- }
476
- /**
477
- * Creates a delta that transforms a base object into a target object.
478
- *
479
- * @description Generates delta instructions that can reconstruct the target
480
- * from the base object. Uses a hash-based algorithm to find matching sequences
481
- * and emits copy/insert instructions accordingly.
482
- *
483
- * **Algorithm:**
484
- * 1. Build a hash table of 4-byte sequences in the base object
485
- * 2. Scan through the target looking for matches in the hash table
486
- * 3. For each match found, verify and extend to maximum length
487
- * 4. Emit copy instructions for matches (4+ bytes)
488
- * 5. Emit insert instructions for non-matching data
489
- *
490
- * **Optimization Notes:**
491
- * - Uses 4-byte window for hash matching
492
- * - Minimum copy size is 4 bytes (smaller copies become inserts)
493
- * - Insert instructions are limited to 127 bytes each
494
- * - Empty base results in pure insert delta
495
- * - Empty target results in headers-only delta
496
- *
497
- * **Output Format:**
498
- * - Source size (varint)
499
- * - Target size (varint)
500
- * - Sequence of copy/insert instructions
501
- *
502
- * @param {Uint8Array} base - The source/base object
503
- * @param {Uint8Array} target - The target object to encode as delta
504
- * @returns {Uint8Array} The delta data (can be applied with {@link applyDelta})
505
- *
506
- * @example
507
- * // Create a delta for similar files
508
- * const v1 = new TextEncoder().encode('Hello, World!');
509
- * const v2 = new TextEncoder().encode('Hello, Universe!');
510
- * const delta = createDelta(v1, v2);
511
- *
512
- * // Delta should be smaller than v2 if there's good overlap
513
- * console.log(`Original: ${v2.length}, Delta: ${delta.length}`);
514
- *
515
- * @example
516
- * // Verify delta correctness
517
- * const reconstructed = applyDelta(v1, delta);
518
- * // reconstructed should equal v2
519
- */
520
- export function createDelta(base, target) {
521
- const instructions = [];
522
- // Add source and target size headers
523
- instructions.push(encodeDeltaSize(base.length));
524
- instructions.push(encodeDeltaSize(target.length));
525
- if (target.length === 0) {
526
- // Empty target, just return headers
527
- return concatArrays(instructions);
528
- }
529
- if (base.length === 0) {
530
- // No base to copy from, insert everything
531
- emitInserts(instructions, target, 0, target.length);
532
- return concatArrays(instructions);
533
- }
534
- // Build optimized hash index using Rabin fingerprints
535
- const index = buildHashIndex(base);
536
- // For large files, process in chunks to limit memory pressure
537
- const isLargeFile = target.length > CHUNK_SIZE;
538
- // Scan target and find matches using rolling hash
539
- let targetOffset = 0;
540
- let insertStart = 0;
541
- // Initialize rolling hash if we have enough bytes
542
- let currentHash = target.length >= WINDOW_SIZE ? rabinHash(target, 0) : 0;
543
- while (targetOffset < target.length) {
544
- let bestMatchOffset = -1;
545
- let bestMatchLength = 0;
546
- // Look for a match if we have enough bytes
547
- if (targetOffset <= target.length - WINDOW_SIZE) {
548
- // Look up candidates from the optimized index
549
- for (const baseOffset of lookupIndex(index, currentHash)) {
550
- // Verify the match and extend it using optimized comparison
551
- const maxLength = Math.min(base.length - baseOffset, target.length - targetOffset);
552
- const matchLength = isLargeFile
553
- ? getMatchLengthOptimized(base, baseOffset, target, targetOffset, maxLength)
554
- : getMatchLength(base, baseOffset, target, targetOffset, maxLength);
555
- if (matchLength >= WINDOW_SIZE && matchLength > bestMatchLength) {
556
- bestMatchOffset = baseOffset;
557
- bestMatchLength = matchLength;
558
- }
559
- }
560
- }
561
- if (bestMatchLength >= MIN_COPY_SIZE) {
562
- // Emit pending inserts
563
- if (targetOffset > insertStart) {
564
- emitInserts(instructions, target, insertStart, targetOffset);
565
- }
566
- // Emit copy instruction
567
- emitCopy(instructions, bestMatchOffset, bestMatchLength);
568
- targetOffset += bestMatchLength;
569
- insertStart = targetOffset;
570
- // Re-compute hash at new position (skip rolling for long jumps)
571
- if (targetOffset <= target.length - WINDOW_SIZE) {
572
- currentHash = rabinHash(target, targetOffset);
573
- }
574
- }
575
- else {
576
- targetOffset++;
577
- // Roll the hash forward (O(1) operation)
578
- if (targetOffset <= target.length - WINDOW_SIZE) {
579
- currentHash = rabinRoll(currentHash, target[targetOffset - 1], target[targetOffset + WINDOW_SIZE - 1]);
580
- }
581
- }
582
- }
583
- // Emit any remaining inserts
584
- if (target.length > insertStart) {
585
- emitInserts(instructions, target, insertStart, target.length);
586
- }
587
- return concatArrays(instructions);
588
- }
589
- /**
590
- * Computes a simple hash of a byte sequence for delta matching.
591
- *
592
- * @description Uses a fast multiplicative hash (similar to djb2) for
593
- * building the hash table used in delta creation.
594
- *
595
- * @param {Uint8Array} data - The data buffer
596
- * @param {number} offset - Starting offset
597
- * @param {number} length - Number of bytes to hash
598
- * @returns {number} 32-bit hash value
599
- * @internal
600
- */
601
- /**
602
- * @internal Reserved for hash table delta compression
603
- */
604
- function _hashBytes(data, offset, length) {
605
- let hash = 0;
606
- for (let i = 0; i < length; i++) {
607
- hash = ((hash << 5) - hash + data[offset + i]) | 0;
608
- }
609
- return hash;
610
- }
611
- void _hashBytes; // Preserve for future delta optimization
612
- /**
613
- * Finds the length of matching bytes between two array regions.
614
- *
615
- * @description Compares bytes starting from the given offsets and returns
616
- * how many consecutive bytes match. Used to extend hash-based matches.
617
- *
618
- * @param {Uint8Array} a - First array
619
- * @param {number} aOffset - Starting offset in first array
620
- * @param {Uint8Array} b - Second array
621
- * @param {number} bOffset - Starting offset in second array
622
- * @param {number} maxLength - Maximum number of bytes to compare
623
- * @returns {number} Number of matching bytes (0 to maxLength)
624
- * @internal
625
- */
626
- function getMatchLength(a, aOffset, b, bOffset, maxLength) {
627
- let length = 0;
628
- while (length < maxLength && a[aOffset + length] === b[bOffset + length]) {
629
- length++;
630
- }
631
- return length;
632
- }
633
- /**
634
- * Emits insert instructions for a range of literal bytes.
635
- *
636
- * @description Insert commands can only encode 1-127 bytes each, so this
637
- * function splits larger ranges into multiple instructions as needed.
638
- *
639
- * @param {Uint8Array[]} instructions - Array to append instructions to
640
- * @param {Uint8Array} data - Source data buffer
641
- * @param {number} start - Starting offset (inclusive)
642
- * @param {number} end - Ending offset (exclusive)
643
- * @internal
644
- */
645
- function emitInserts(instructions, data, start, end) {
646
- const MAX_INSERT = 127;
647
- let offset = start;
648
- while (offset < end) {
649
- const size = Math.min(MAX_INSERT, end - offset);
650
- const instruction = new Uint8Array(1 + size);
651
- instruction[0] = size; // Insert command: size in lower 7 bits
652
- instruction.set(data.subarray(offset, offset + size), 1);
653
- instructions.push(instruction);
654
- offset += size;
655
- }
656
- }
657
- /**
658
- * Emits a copy instruction that copies bytes from the base object.
659
- *
660
- * @description Encodes a copy instruction using Git's compact format where
661
- * only non-zero offset and size bytes are included, indicated by bit flags.
662
- *
663
- * **Encoding Details:**
664
- * - Offset bytes (up to 4) are included based on bits 0-3 of command byte
665
- * - Size bytes (up to 3) are included based on bits 4-6 of command byte
666
- * - Size of 0x10000 (65536) is encoded as no size bytes (size=0 means 0x10000)
667
- * - Offset of 0 means no offset bytes are included
668
- *
669
- * @param {Uint8Array[]} instructions - Array to append the instruction to
670
- * @param {number} offset - Byte offset in base object to copy from
671
- * @param {number} size - Number of bytes to copy
672
- * @internal
673
- */
674
- function emitCopy(instructions, offset, size) {
675
- const bytes = [];
676
- let cmd = COPY_INSTRUCTION;
677
- // Encode offset bytes (little-endian)
678
- if (offset & 0xff) {
679
- cmd |= 0x01;
680
- bytes.push(offset & 0xff);
681
- }
682
- if (offset & 0xff00) {
683
- cmd |= 0x02;
684
- bytes.push((offset >> 8) & 0xff);
685
- }
686
- if (offset & 0xff0000) {
687
- cmd |= 0x04;
688
- bytes.push((offset >> 16) & 0xff);
689
- }
690
- if (offset & 0xff000000) {
691
- cmd |= 0x08;
692
- bytes.push((offset >> 24) & 0xff);
693
- }
694
- // Special case: if offset is 0, we don't emit any offset bytes
695
- // The cmd byte already indicates no offset bytes are present
696
- // Encode size bytes (little-endian)
697
- // Note: size of 0x10000 is encoded as no size bytes (all zero)
698
- if (size !== 0x10000) {
699
- if (size & 0xff) {
700
- cmd |= 0x10;
701
- bytes.push(size & 0xff);
702
- }
703
- if (size & 0xff00) {
704
- cmd |= 0x20;
705
- bytes.push((size >> 8) & 0xff);
706
- }
707
- if (size & 0xff0000) {
708
- cmd |= 0x40;
709
- bytes.push((size >> 16) & 0xff);
710
- }
711
- }
712
- // If size is 0x10000, we don't set any size bits, which encodes as size=0x10000
713
- const instruction = new Uint8Array(1 + bytes.length);
714
- instruction[0] = cmd;
715
- for (let i = 0; i < bytes.length; i++) {
716
- instruction[1 + i] = bytes[i];
717
- }
718
- instructions.push(instruction);
719
- }
720
- /**
721
- * Concatenates multiple Uint8Arrays into a single array.
722
- *
723
- * @param {Uint8Array[]} arrays - Arrays to concatenate
724
- * @returns {Uint8Array} Combined array
725
- * @internal
726
- */
727
- function concatArrays(arrays) {
728
- let totalLength = 0;
729
- for (const arr of arrays) {
730
- totalLength += arr.length;
731
- }
732
- const result = new Uint8Array(totalLength);
733
- let offset = 0;
734
- for (const arr of arrays) {
735
- result.set(arr, offset);
736
- offset += arr.length;
737
- }
738
- return result;
739
- }
740
- //# sourceMappingURL=delta.js.map