@hot-updater/bsdiff 0.30.12 → 0.31.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/LICENSE CHANGED
@@ -1,6 +1,13 @@
1
+ Hot Updater bsdiff package
2
+
3
+ This package is distributed under the MIT license for Hot Updater package code
4
+ and includes BSD-2-Clause licensed bsdiff functionality plus bzip2-licensed
5
+ compression functionality in its compiled WASM artifact. See NOTICE.md for
6
+ source URLs and dependency attribution.
7
+
1
8
  MIT License
2
9
 
3
- Copyright (c) 2023 Sungyu Kang
10
+ Copyright (c) 2025-present Hot Updater contributors
4
11
 
5
12
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
13
  of this software and associated documentation files (the "Software"), to deal
@@ -20,14 +27,93 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
27
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
28
  SOFTWARE.
22
29
 
23
- ---
30
+ BSD 2-Clause License
31
+
32
+ Copyright 2003-2005 Colin Percival
33
+ Copyright 2012 Matthew Endsley
34
+ Modified 2017 Pieter-Jan Briers
35
+ Modified 2021 Kornel Lesinski
36
+ All rights reserved
37
+
38
+ Redistribution and use in source and binary forms, with or without
39
+ modification, are permitted providing that the following conditions are met:
40
+
41
+ 1. Redistributions of source code must retain the above copyright notice, this
42
+ list of conditions and the following disclaimer.
43
+
44
+ 2. Redistributions in binary form must reproduce the above copyright notice,
45
+ this list of conditions and the following disclaimer in the documentation
46
+ and/or other materials provided with the distribution.
47
+
48
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
49
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
50
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
51
+ EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
52
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
53
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
54
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
55
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
57
+ POSSIBILITY OF SUCH DAMAGE.
58
+
59
+ MIT License for bzip2 crate
60
+
61
+ Copyright (c) 2014-2025 Alex Crichton and Contributors
62
+
63
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
64
+ this software and associated documentation files (the "Software"), to deal in the
65
+ Software without restriction, including without limitation the rights to use,
66
+ copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
67
+ Software, and to permit persons to whom the Software is furnished to do so,
68
+ subject to the following conditions:
69
+
70
+ The above copyright notice and this permission notice shall be included in all
71
+ copies or substantial portions of the Software.
72
+
73
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
74
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
75
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
76
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
77
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
78
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
79
+
80
+ bzip2-1.0.6 License for libbzip2-rs
81
+
82
+ The original program, "bzip2", the associated library "libbzip2", and all
83
+ documentation, are
84
+
85
+ Copyright (C) 1996-2021 Julian R Seward.
86
+ Copyright (C) 2019-2020 Federico Mena Quintero
87
+ Copyright (C) 2021 Micah Snyder
88
+
89
+ This Rust translation, "libbzip2-rs" is a derived work based on "bzip2" and
90
+ "libbzip2", and is Copyright (C) 2024-2025 Trifecta Tech Foundation and
91
+ contributors
92
+
93
+ Redistribution and use in source and binary forms, with or without
94
+ modification, are permitted provided that the following conditions are met:
95
+
96
+ 1. Redistributions of source code must retain the above copyright notice, this
97
+ list of conditions and the following disclaimer.
98
+
99
+ 2. The origin of this software must not be misrepresented; you must not claim
100
+ that you wrote the original software. If you use this software in a product,
101
+ an acknowledgment in the product documentation would be appreciated but is
102
+ not required.
24
103
 
25
- ADDITIONAL DISCLAIMER
104
+ 3. Altered source versions must be plainly marked as such, and must not be
105
+ misrepresented as being the original software.
26
106
 
27
- The authors of this software shall not be held responsible for any legal issues,
28
- regulatory violations, policy violations, or disputes arising from the use,
29
- distribution, or integration of this software.
107
+ 4. The name of the author may not be used to endorse or promote products
108
+ derived from this software without specific prior written permission.
30
109
 
31
- Users are solely responsible for ensuring compliance with all applicable laws,
32
- regulations, and third-party policies (including but not limited to app store
33
- policies).
110
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
111
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
112
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
113
+ SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
114
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
115
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
116
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
117
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
118
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
119
+ DAMAGE.
package/NOTICE.md ADDED
@@ -0,0 +1,52 @@
1
+ # Notices for @hot-updater/bsdiff
2
+
3
+ This package contains Hot Updater code plus a precompiled WebAssembly artifact
4
+ that incorporates third-party bsdiff and bzip2 implementations.
5
+
6
+ ## Hot Updater package code
7
+
8
+ - Source: https://github.com/gronxb/hot-updater
9
+ - License: MIT
10
+ - Copyright: 2025-present Hot Updater contributors
11
+
12
+ ## bsdiff
13
+
14
+ - Crate: `bsdiff` 0.2.1
15
+ - Source: https://github.com/space-wizards/bsdiff-rs
16
+ - Crates.io: https://crates.io/crates/bsdiff
17
+ - License: BSD-2-Clause
18
+
19
+ The Rust crate is a port of Matthew Endsley's bsdiff library:
20
+
21
+ - Source: https://github.com/mendsley/bsdiff
22
+ - Copyright: 2003-2005 Colin Percival
23
+ - Copyright: 2012 Matthew Endsley
24
+ - Modified: 2017 Pieter-Jan Briers
25
+ - Modified: 2021 Kornel Lesinski
26
+
27
+ Hot Updater uses this implementation to produce and apply the control/diff/extra
28
+ stream that is wrapped as an `ENDSLEY/BSDIFF43` patch.
29
+
30
+ ## bzip2-rs
31
+
32
+ - Crate: `bzip2` 0.6.1
33
+ - Source: https://github.com/trifectatechfoundation/bzip2-rs
34
+ - Crates.io: https://crates.io/crates/bzip2
35
+ - License: MIT OR Apache-2.0
36
+ - Copyright: 2014-2025 Alex Crichton and Contributors
37
+
38
+ This distribution satisfies the `bzip2` crate's dual license under the MIT
39
+ license option.
40
+
41
+ ## libbzip2-rs
42
+
43
+ - Crate: `libbz2-rs-sys` 0.2.2
44
+ - Source: https://github.com/trifectatechfoundation/libbzip2-rs
45
+ - Crates.io: https://crates.io/crates/libbz2-rs-sys
46
+ - License: bzip2-1.0.6
47
+ - Copyright: 1996-2021 Julian R Seward
48
+ - Copyright: 2019-2020 Federico Mena Quintero
49
+ - Copyright: 2021 Micah Snyder
50
+ - Copyright: 2024-2025 Trifecta Tech Foundation and contributors
51
+
52
+ `libbzip2-rs` is a Rust translation and derived work based on bzip2/libbzip2.
package/assets/hdiff.wasm CHANGED
Binary file
@@ -50,13 +50,13 @@ function readAndFreeOutput(wasm) {
50
50
  }
51
51
  function mapPatchError(status) {
52
52
  if (status === BsdiffStatus.INVALID_INPUT) return new HdiffError("PATCH_FAILED", "Invalid input bytes provided to bsdiff wasm");
53
- if (status === BsdiffStatus.PATCH_FAILED) return new HdiffError("PATCH_FAILED", "Failed to generate BSDIFF40 patch");
53
+ if (status === BsdiffStatus.PATCH_FAILED) return new HdiffError("PATCH_FAILED", "Failed to generate ENDSLEY/BSDIFF43 patch");
54
54
  if (status === BsdiffStatus.INVALID_PATCH) return new HdiffError("PATCH_FAILED", "Unexpected patch-validation status while creating patch");
55
55
  return new HdiffError("PATCH_FAILED", `Unknown bsdiff wasm status: ${status}`);
56
56
  }
57
57
  function mapApplyError(status) {
58
58
  if (status === BsdiffStatus.INVALID_INPUT) return new HdiffError("PATCH_FAILED", "Invalid input bytes provided to bspatch wasm");
59
- if (status === BsdiffStatus.INVALID_PATCH) return new HdiffError("PATCH_FAILED", "Invalid BSDIFF40 patch bytes");
59
+ if (status === BsdiffStatus.INVALID_PATCH) return new HdiffError("PATCH_FAILED", "Invalid ENDSLEY/BSDIFF43 patch bytes");
60
60
  if (status === BsdiffStatus.PATCH_FAILED) return new HdiffError("PATCH_FAILED", "Unexpected patch-generation status while applying patch");
61
61
  return new HdiffError("PATCH_FAILED", `Unknown bspatch wasm status: ${status}`);
62
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bsdiff.js","names":[],"sources":["../../src/internal/bsdiff.ts"],"sourcesContent":["import { HdiffError } from \"../errors.js\";\nimport type { Bytes } from \"./bytes.js\";\n\ntype BsdiffExports = {\n memory: WebAssembly.Memory;\n alloc: (len: number) => number;\n dealloc: (ptr: number, len: number) => void;\n create_patch: (\n basePtr: number,\n baseLen: number,\n nextPtr: number,\n nextLen: number,\n ) => number;\n apply_patch: (\n basePtr: number,\n baseLen: number,\n patchPtr: number,\n patchLen: number,\n ) => number;\n output_ptr: () => number;\n output_len: () => number;\n free_output: () => void;\n};\n\nenum BsdiffStatus {\n OK = 0,\n INVALID_INPUT = 1,\n PATCH_FAILED = 2,\n INVALID_PATCH = 3,\n}\n\nlet wasmPromise: Promise<BsdiffExports> | undefined;\n\nexport async function createBsdiffPatch(\n base: Bytes,\n next: Bytes,\n): Promise<Bytes> {\n const wasm = await getBsdiffWasm();\n\n const status = runBinaryOperation(\n wasm,\n base,\n next,\n (basePtr, baseLen, nextPtr, nextLen) =>\n wasm.create_patch(basePtr, baseLen, nextPtr, nextLen),\n );\n\n if (status !== BsdiffStatus.OK) {\n throw mapPatchError(status);\n }\n\n return readAndFreeOutput(wasm);\n}\n\nexport async function applyBsdiffPatch(\n base: Bytes,\n patch: Bytes,\n): Promise<Bytes> {\n const wasm = await getBsdiffWasm();\n\n const status = runBinaryOperation(\n wasm,\n base,\n patch,\n (basePtr, baseLen, patchPtr, patchLen) =>\n wasm.apply_patch(basePtr, baseLen, patchPtr, patchLen),\n );\n\n if (status !== BsdiffStatus.OK) {\n throw mapApplyError(status);\n }\n\n return readAndFreeOutput(wasm);\n}\n\nfunction runBinaryOperation(\n wasm: BsdiffExports,\n left: Bytes,\n right: Bytes,\n run: (\n leftPtr: number,\n leftLen: number,\n rightPtr: number,\n rightLen: number,\n ) => number,\n): number {\n const leftPtr = wasm.alloc(left.byteLength);\n const rightPtr = wasm.alloc(right.byteLength);\n\n try {\n writeBytesToMemory(wasm.memory, leftPtr, left);\n writeBytesToMemory(wasm.memory, rightPtr, right);\n return run(leftPtr, left.byteLength, rightPtr, right.byteLength);\n } finally {\n if (left.byteLength > 0) {\n wasm.dealloc(leftPtr, left.byteLength);\n }\n if (right.byteLength > 0) {\n wasm.dealloc(rightPtr, right.byteLength);\n }\n }\n}\n\nfunction writeBytesToMemory(\n memory: WebAssembly.Memory,\n ptr: number,\n bytes: Bytes,\n): void {\n if (bytes.byteLength === 0) {\n return;\n }\n new Uint8Array(memory.buffer, ptr, bytes.byteLength).set(bytes);\n}\n\nfunction readAndFreeOutput(wasm: BsdiffExports): Bytes {\n const outPtr = wasm.output_ptr();\n const outLen = wasm.output_len();\n\n if (outLen === 0) {\n wasm.free_output();\n return new Uint8Array();\n }\n\n const output = new Uint8Array(wasm.memory.buffer, outPtr, outLen);\n const copied = new Uint8Array(output);\n wasm.free_output();\n return copied;\n}\n\nfunction mapPatchError(status: number): HdiffError {\n if (status === BsdiffStatus.INVALID_INPUT) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Invalid input bytes provided to bsdiff wasm\",\n );\n }\n if (status === BsdiffStatus.PATCH_FAILED) {\n return new HdiffError(\"PATCH_FAILED\", \"Failed to generate BSDIFF40 patch\");\n }\n if (status === BsdiffStatus.INVALID_PATCH) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Unexpected patch-validation status while creating patch\",\n );\n }\n return new HdiffError(\n \"PATCH_FAILED\",\n `Unknown bsdiff wasm status: ${status}`,\n );\n}\n\nfunction mapApplyError(status: number): HdiffError {\n if (status === BsdiffStatus.INVALID_INPUT) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Invalid input bytes provided to bspatch wasm\",\n );\n }\n if (status === BsdiffStatus.INVALID_PATCH) {\n return new HdiffError(\"PATCH_FAILED\", \"Invalid BSDIFF40 patch bytes\");\n }\n if (status === BsdiffStatus.PATCH_FAILED) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Unexpected patch-generation status while applying patch\",\n );\n }\n return new HdiffError(\n \"PATCH_FAILED\",\n `Unknown bspatch wasm status: ${status}`,\n );\n}\n\nasync function getBsdiffWasm(): Promise<BsdiffExports> {\n wasmPromise ??= loadBsdiffWasm();\n return wasmPromise;\n}\n\nasync function loadBsdiffWasm(): Promise<BsdiffExports> {\n const precompiled = getPrecompiledBsdiffModule();\n const directExports = asBsdiffExports(precompiled);\n if (directExports) {\n return directExports;\n }\n\n if (isWasmModuleLike(precompiled)) {\n const instance = await WebAssembly.instantiate(precompiled);\n return toBsdiffExports(instance.exports);\n }\n\n throw new HdiffError(\n \"PATCH_FAILED\",\n \"No precompiled bsdiff WASM configured. Import a runtime entry such as @hot-updater/bsdiff/node, /bun, /deno, or /worker.\",\n );\n}\n\nfunction getPrecompiledBsdiffModule(): unknown {\n const hdiffGlobal = globalThis as {\n __HDIFF_PRECOMPILED_WASM__?: unknown;\n __HDIFF_PRECOMPILED_BSDIFF_WASM__?: unknown;\n };\n\n const mod =\n hdiffGlobal.__HDIFF_PRECOMPILED_BSDIFF_WASM__ ??\n hdiffGlobal.__HDIFF_PRECOMPILED_WASM__;\n if (mod && typeof mod === \"object\" && \"default\" in mod) {\n return (mod as { default?: unknown }).default;\n }\n return mod;\n}\n\nfunction isWasmModuleLike(mod: unknown): mod is WebAssembly.Module {\n if (mod instanceof WebAssembly.Module) {\n return true;\n }\n return Object.prototype.toString.call(mod) === \"[object WebAssembly.Module]\";\n}\n\nfunction isWasmMemoryLike(memory: unknown): memory is WebAssembly.Memory {\n if (memory instanceof WebAssembly.Memory) {\n return true;\n }\n return (\n Object.prototype.toString.call(memory) === \"[object WebAssembly.Memory]\"\n );\n}\n\nfunction toBsdiffExports(source: unknown): BsdiffExports {\n const direct = asBsdiffExports(source);\n if (direct) {\n return direct;\n }\n\n throw new HdiffError(\"PATCH_FAILED\", \"bsdiff wasm exports are incomplete\");\n}\n\nfunction asBsdiffExports(source: unknown): BsdiffExports | undefined {\n if (source && typeof source === \"object\") {\n const maybe = source as Partial<BsdiffExports>;\n if (\n isWasmMemoryLike(maybe.memory) &&\n typeof maybe.alloc === \"function\" &&\n typeof maybe.dealloc === \"function\" &&\n typeof maybe.create_patch === \"function\" &&\n typeof maybe.apply_patch === \"function\" &&\n typeof maybe.output_ptr === \"function\" &&\n typeof maybe.output_len === \"function\" &&\n typeof maybe.free_output === \"function\"\n ) {\n return maybe as BsdiffExports;\n }\n }\n return undefined;\n}\n"],"mappings":";;AAwBA,IAAK,eAAL,yBAAA,cAAA;AACE,cAAA,aAAA,QAAA,KAAA;AACA,cAAA,aAAA,mBAAA,KAAA;AACA,cAAA,aAAA,kBAAA,KAAA;AACA,cAAA,aAAA,mBAAA,KAAA;;EAJG,gBAAA,EAAA,CAKJ;AAED,IAAI;AAEJ,eAAsB,kBACpB,MACA,MACgB;CAChB,MAAM,OAAO,MAAM,eAAe;CAElC,MAAM,SAAS,mBACb,MACA,MACA,OACC,SAAS,SAAS,SAAS,YAC1B,KAAK,aAAa,SAAS,SAAS,SAAS,QAAQ,CACxD;AAED,KAAI,WAAW,aAAa,GAC1B,OAAM,cAAc,OAAO;AAG7B,QAAO,kBAAkB,KAAK;;AAGhC,eAAsB,iBACpB,MACA,OACgB;CAChB,MAAM,OAAO,MAAM,eAAe;CAElC,MAAM,SAAS,mBACb,MACA,MACA,QACC,SAAS,SAAS,UAAU,aAC3B,KAAK,YAAY,SAAS,SAAS,UAAU,SAAS,CACzD;AAED,KAAI,WAAW,aAAa,GAC1B,OAAM,cAAc,OAAO;AAG7B,QAAO,kBAAkB,KAAK;;AAGhC,SAAS,mBACP,MACA,MACA,OACA,KAMQ;CACR,MAAM,UAAU,KAAK,MAAM,KAAK,WAAW;CAC3C,MAAM,WAAW,KAAK,MAAM,MAAM,WAAW;AAE7C,KAAI;AACF,qBAAmB,KAAK,QAAQ,SAAS,KAAK;AAC9C,qBAAmB,KAAK,QAAQ,UAAU,MAAM;AAChD,SAAO,IAAI,SAAS,KAAK,YAAY,UAAU,MAAM,WAAW;WACxD;AACR,MAAI,KAAK,aAAa,EACpB,MAAK,QAAQ,SAAS,KAAK,WAAW;AAExC,MAAI,MAAM,aAAa,EACrB,MAAK,QAAQ,UAAU,MAAM,WAAW;;;AAK9C,SAAS,mBACP,QACA,KACA,OACM;AACN,KAAI,MAAM,eAAe,EACvB;AAEF,KAAI,WAAW,OAAO,QAAQ,KAAK,MAAM,WAAW,CAAC,IAAI,MAAM;;AAGjE,SAAS,kBAAkB,MAA4B;CACrD,MAAM,SAAS,KAAK,YAAY;CAChC,MAAM,SAAS,KAAK,YAAY;AAEhC,KAAI,WAAW,GAAG;AAChB,OAAK,aAAa;AAClB,SAAO,IAAI,YAAY;;CAGzB,MAAM,SAAS,IAAI,WAAW,KAAK,OAAO,QAAQ,QAAQ,OAAO;CACjE,MAAM,SAAS,IAAI,WAAW,OAAO;AACrC,MAAK,aAAa;AAClB,QAAO;;AAGT,SAAS,cAAc,QAA4B;AACjD,KAAI,WAAW,aAAa,cAC1B,QAAO,IAAI,WACT,gBACA,8CACD;AAEH,KAAI,WAAW,aAAa,aAC1B,QAAO,IAAI,WAAW,gBAAgB,oCAAoC;AAE5E,KAAI,WAAW,aAAa,cAC1B,QAAO,IAAI,WACT,gBACA,0DACD;AAEH,QAAO,IAAI,WACT,gBACA,+BAA+B,SAChC;;AAGH,SAAS,cAAc,QAA4B;AACjD,KAAI,WAAW,aAAa,cAC1B,QAAO,IAAI,WACT,gBACA,+CACD;AAEH,KAAI,WAAW,aAAa,cAC1B,QAAO,IAAI,WAAW,gBAAgB,+BAA+B;AAEvE,KAAI,WAAW,aAAa,aAC1B,QAAO,IAAI,WACT,gBACA,0DACD;AAEH,QAAO,IAAI,WACT,gBACA,gCAAgC,SACjC;;AAGH,eAAe,gBAAwC;AACrD,iBAAgB,gBAAgB;AAChC,QAAO;;AAGT,eAAe,iBAAyC;CACtD,MAAM,cAAc,4BAA4B;CAChD,MAAM,gBAAgB,gBAAgB,YAAY;AAClD,KAAI,cACF,QAAO;AAGT,KAAI,iBAAiB,YAAY,CAE/B,QAAO,iBADU,MAAM,YAAY,YAAY,YAAY,EAC3B,QAAQ;AAG1C,OAAM,IAAI,WACR,gBACA,2HACD;;AAGH,SAAS,6BAAsC;CAC7C,MAAM,cAAc;CAKpB,MAAM,MACJ,YAAY,qCACZ,YAAY;AACd,KAAI,OAAO,OAAO,QAAQ,YAAY,aAAa,IACjD,QAAQ,IAA8B;AAExC,QAAO;;AAGT,SAAS,iBAAiB,KAAyC;AACjE,KAAI,eAAe,YAAY,OAC7B,QAAO;AAET,QAAO,OAAO,UAAU,SAAS,KAAK,IAAI,KAAK;;AAGjD,SAAS,iBAAiB,QAA+C;AACvE,KAAI,kBAAkB,YAAY,OAChC,QAAO;AAET,QACE,OAAO,UAAU,SAAS,KAAK,OAAO,KAAK;;AAI/C,SAAS,gBAAgB,QAAgC;CACvD,MAAM,SAAS,gBAAgB,OAAO;AACtC,KAAI,OACF,QAAO;AAGT,OAAM,IAAI,WAAW,gBAAgB,qCAAqC;;AAG5E,SAAS,gBAAgB,QAA4C;AACnE,KAAI,UAAU,OAAO,WAAW,UAAU;EACxC,MAAM,QAAQ;AACd,MACE,iBAAiB,MAAM,OAAO,IAC9B,OAAO,MAAM,UAAU,cACvB,OAAO,MAAM,YAAY,cACzB,OAAO,MAAM,iBAAiB,cAC9B,OAAO,MAAM,gBAAgB,cAC7B,OAAO,MAAM,eAAe,cAC5B,OAAO,MAAM,eAAe,cAC5B,OAAO,MAAM,gBAAgB,WAE7B,QAAO"}
1
+ {"version":3,"file":"bsdiff.js","names":[],"sources":["../../src/internal/bsdiff.ts"],"sourcesContent":["import { HdiffError } from \"../errors.js\";\nimport type { Bytes } from \"./bytes.js\";\n\ntype BsdiffExports = {\n memory: WebAssembly.Memory;\n alloc: (len: number) => number;\n dealloc: (ptr: number, len: number) => void;\n create_patch: (\n basePtr: number,\n baseLen: number,\n nextPtr: number,\n nextLen: number,\n ) => number;\n apply_patch: (\n basePtr: number,\n baseLen: number,\n patchPtr: number,\n patchLen: number,\n ) => number;\n output_ptr: () => number;\n output_len: () => number;\n free_output: () => void;\n};\n\nenum BsdiffStatus {\n OK = 0,\n INVALID_INPUT = 1,\n PATCH_FAILED = 2,\n INVALID_PATCH = 3,\n}\n\nlet wasmPromise: Promise<BsdiffExports> | undefined;\n\nexport async function createBsdiffPatch(\n base: Bytes,\n next: Bytes,\n): Promise<Bytes> {\n const wasm = await getBsdiffWasm();\n\n const status = runBinaryOperation(\n wasm,\n base,\n next,\n (basePtr, baseLen, nextPtr, nextLen) =>\n wasm.create_patch(basePtr, baseLen, nextPtr, nextLen),\n );\n\n if (status !== BsdiffStatus.OK) {\n throw mapPatchError(status);\n }\n\n return readAndFreeOutput(wasm);\n}\n\nexport async function applyBsdiffPatch(\n base: Bytes,\n patch: Bytes,\n): Promise<Bytes> {\n const wasm = await getBsdiffWasm();\n\n const status = runBinaryOperation(\n wasm,\n base,\n patch,\n (basePtr, baseLen, patchPtr, patchLen) =>\n wasm.apply_patch(basePtr, baseLen, patchPtr, patchLen),\n );\n\n if (status !== BsdiffStatus.OK) {\n throw mapApplyError(status);\n }\n\n return readAndFreeOutput(wasm);\n}\n\nfunction runBinaryOperation(\n wasm: BsdiffExports,\n left: Bytes,\n right: Bytes,\n run: (\n leftPtr: number,\n leftLen: number,\n rightPtr: number,\n rightLen: number,\n ) => number,\n): number {\n const leftPtr = wasm.alloc(left.byteLength);\n const rightPtr = wasm.alloc(right.byteLength);\n\n try {\n writeBytesToMemory(wasm.memory, leftPtr, left);\n writeBytesToMemory(wasm.memory, rightPtr, right);\n return run(leftPtr, left.byteLength, rightPtr, right.byteLength);\n } finally {\n if (left.byteLength > 0) {\n wasm.dealloc(leftPtr, left.byteLength);\n }\n if (right.byteLength > 0) {\n wasm.dealloc(rightPtr, right.byteLength);\n }\n }\n}\n\nfunction writeBytesToMemory(\n memory: WebAssembly.Memory,\n ptr: number,\n bytes: Bytes,\n): void {\n if (bytes.byteLength === 0) {\n return;\n }\n new Uint8Array(memory.buffer, ptr, bytes.byteLength).set(bytes);\n}\n\nfunction readAndFreeOutput(wasm: BsdiffExports): Bytes {\n const outPtr = wasm.output_ptr();\n const outLen = wasm.output_len();\n\n if (outLen === 0) {\n wasm.free_output();\n return new Uint8Array();\n }\n\n const output = new Uint8Array(wasm.memory.buffer, outPtr, outLen);\n const copied = new Uint8Array(output);\n wasm.free_output();\n return copied;\n}\n\nfunction mapPatchError(status: number): HdiffError {\n if (status === BsdiffStatus.INVALID_INPUT) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Invalid input bytes provided to bsdiff wasm\",\n );\n }\n if (status === BsdiffStatus.PATCH_FAILED) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Failed to generate ENDSLEY/BSDIFF43 patch\",\n );\n }\n if (status === BsdiffStatus.INVALID_PATCH) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Unexpected patch-validation status while creating patch\",\n );\n }\n return new HdiffError(\n \"PATCH_FAILED\",\n `Unknown bsdiff wasm status: ${status}`,\n );\n}\n\nfunction mapApplyError(status: number): HdiffError {\n if (status === BsdiffStatus.INVALID_INPUT) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Invalid input bytes provided to bspatch wasm\",\n );\n }\n if (status === BsdiffStatus.INVALID_PATCH) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Invalid ENDSLEY/BSDIFF43 patch bytes\",\n );\n }\n if (status === BsdiffStatus.PATCH_FAILED) {\n return new HdiffError(\n \"PATCH_FAILED\",\n \"Unexpected patch-generation status while applying patch\",\n );\n }\n return new HdiffError(\n \"PATCH_FAILED\",\n `Unknown bspatch wasm status: ${status}`,\n );\n}\n\nasync function getBsdiffWasm(): Promise<BsdiffExports> {\n wasmPromise ??= loadBsdiffWasm();\n return wasmPromise;\n}\n\nasync function loadBsdiffWasm(): Promise<BsdiffExports> {\n const precompiled = getPrecompiledBsdiffModule();\n const directExports = asBsdiffExports(precompiled);\n if (directExports) {\n return directExports;\n }\n\n if (isWasmModuleLike(precompiled)) {\n const instance = await WebAssembly.instantiate(precompiled);\n return toBsdiffExports(instance.exports);\n }\n\n throw new HdiffError(\n \"PATCH_FAILED\",\n \"No precompiled bsdiff WASM configured. Import a runtime entry such as @hot-updater/bsdiff/node, /bun, /deno, or /worker.\",\n );\n}\n\nfunction getPrecompiledBsdiffModule(): unknown {\n const hdiffGlobal = globalThis as {\n __HDIFF_PRECOMPILED_WASM__?: unknown;\n __HDIFF_PRECOMPILED_BSDIFF_WASM__?: unknown;\n };\n\n const mod =\n hdiffGlobal.__HDIFF_PRECOMPILED_BSDIFF_WASM__ ??\n hdiffGlobal.__HDIFF_PRECOMPILED_WASM__;\n if (mod && typeof mod === \"object\" && \"default\" in mod) {\n return (mod as { default?: unknown }).default;\n }\n return mod;\n}\n\nfunction isWasmModuleLike(mod: unknown): mod is WebAssembly.Module {\n if (mod instanceof WebAssembly.Module) {\n return true;\n }\n return Object.prototype.toString.call(mod) === \"[object WebAssembly.Module]\";\n}\n\nfunction isWasmMemoryLike(memory: unknown): memory is WebAssembly.Memory {\n if (memory instanceof WebAssembly.Memory) {\n return true;\n }\n return (\n Object.prototype.toString.call(memory) === \"[object WebAssembly.Memory]\"\n );\n}\n\nfunction toBsdiffExports(source: unknown): BsdiffExports {\n const direct = asBsdiffExports(source);\n if (direct) {\n return direct;\n }\n\n throw new HdiffError(\"PATCH_FAILED\", \"bsdiff wasm exports are incomplete\");\n}\n\nfunction asBsdiffExports(source: unknown): BsdiffExports | undefined {\n if (source && typeof source === \"object\") {\n const maybe = source as Partial<BsdiffExports>;\n if (\n isWasmMemoryLike(maybe.memory) &&\n typeof maybe.alloc === \"function\" &&\n typeof maybe.dealloc === \"function\" &&\n typeof maybe.create_patch === \"function\" &&\n typeof maybe.apply_patch === \"function\" &&\n typeof maybe.output_ptr === \"function\" &&\n typeof maybe.output_len === \"function\" &&\n typeof maybe.free_output === \"function\"\n ) {\n return maybe as BsdiffExports;\n }\n }\n return undefined;\n}\n"],"mappings":";;AAwBA,IAAK,eAAL,yBAAA,cAAA;AACE,cAAA,aAAA,QAAA,KAAA;AACA,cAAA,aAAA,mBAAA,KAAA;AACA,cAAA,aAAA,kBAAA,KAAA;AACA,cAAA,aAAA,mBAAA,KAAA;;EAJG,gBAAA,EAAA,CAKJ;AAED,IAAI;AAEJ,eAAsB,kBACpB,MACA,MACgB;CAChB,MAAM,OAAO,MAAM,eAAe;CAElC,MAAM,SAAS,mBACb,MACA,MACA,OACC,SAAS,SAAS,SAAS,YAC1B,KAAK,aAAa,SAAS,SAAS,SAAS,QAAQ,CACxD;AAED,KAAI,WAAW,aAAa,GAC1B,OAAM,cAAc,OAAO;AAG7B,QAAO,kBAAkB,KAAK;;AAGhC,eAAsB,iBACpB,MACA,OACgB;CAChB,MAAM,OAAO,MAAM,eAAe;CAElC,MAAM,SAAS,mBACb,MACA,MACA,QACC,SAAS,SAAS,UAAU,aAC3B,KAAK,YAAY,SAAS,SAAS,UAAU,SAAS,CACzD;AAED,KAAI,WAAW,aAAa,GAC1B,OAAM,cAAc,OAAO;AAG7B,QAAO,kBAAkB,KAAK;;AAGhC,SAAS,mBACP,MACA,MACA,OACA,KAMQ;CACR,MAAM,UAAU,KAAK,MAAM,KAAK,WAAW;CAC3C,MAAM,WAAW,KAAK,MAAM,MAAM,WAAW;AAE7C,KAAI;AACF,qBAAmB,KAAK,QAAQ,SAAS,KAAK;AAC9C,qBAAmB,KAAK,QAAQ,UAAU,MAAM;AAChD,SAAO,IAAI,SAAS,KAAK,YAAY,UAAU,MAAM,WAAW;WACxD;AACR,MAAI,KAAK,aAAa,EACpB,MAAK,QAAQ,SAAS,KAAK,WAAW;AAExC,MAAI,MAAM,aAAa,EACrB,MAAK,QAAQ,UAAU,MAAM,WAAW;;;AAK9C,SAAS,mBACP,QACA,KACA,OACM;AACN,KAAI,MAAM,eAAe,EACvB;AAEF,KAAI,WAAW,OAAO,QAAQ,KAAK,MAAM,WAAW,CAAC,IAAI,MAAM;;AAGjE,SAAS,kBAAkB,MAA4B;CACrD,MAAM,SAAS,KAAK,YAAY;CAChC,MAAM,SAAS,KAAK,YAAY;AAEhC,KAAI,WAAW,GAAG;AAChB,OAAK,aAAa;AAClB,SAAO,IAAI,YAAY;;CAGzB,MAAM,SAAS,IAAI,WAAW,KAAK,OAAO,QAAQ,QAAQ,OAAO;CACjE,MAAM,SAAS,IAAI,WAAW,OAAO;AACrC,MAAK,aAAa;AAClB,QAAO;;AAGT,SAAS,cAAc,QAA4B;AACjD,KAAI,WAAW,aAAa,cAC1B,QAAO,IAAI,WACT,gBACA,8CACD;AAEH,KAAI,WAAW,aAAa,aAC1B,QAAO,IAAI,WACT,gBACA,4CACD;AAEH,KAAI,WAAW,aAAa,cAC1B,QAAO,IAAI,WACT,gBACA,0DACD;AAEH,QAAO,IAAI,WACT,gBACA,+BAA+B,SAChC;;AAGH,SAAS,cAAc,QAA4B;AACjD,KAAI,WAAW,aAAa,cAC1B,QAAO,IAAI,WACT,gBACA,+CACD;AAEH,KAAI,WAAW,aAAa,cAC1B,QAAO,IAAI,WACT,gBACA,uCACD;AAEH,KAAI,WAAW,aAAa,aAC1B,QAAO,IAAI,WACT,gBACA,0DACD;AAEH,QAAO,IAAI,WACT,gBACA,gCAAgC,SACjC;;AAGH,eAAe,gBAAwC;AACrD,iBAAgB,gBAAgB;AAChC,QAAO;;AAGT,eAAe,iBAAyC;CACtD,MAAM,cAAc,4BAA4B;CAChD,MAAM,gBAAgB,gBAAgB,YAAY;AAClD,KAAI,cACF,QAAO;AAGT,KAAI,iBAAiB,YAAY,CAE/B,QAAO,iBADU,MAAM,YAAY,YAAY,YAAY,EAC3B,QAAQ;AAG1C,OAAM,IAAI,WACR,gBACA,2HACD;;AAGH,SAAS,6BAAsC;CAC7C,MAAM,cAAc;CAKpB,MAAM,MACJ,YAAY,qCACZ,YAAY;AACd,KAAI,OAAO,OAAO,QAAQ,YAAY,aAAa,IACjD,QAAQ,IAA8B;AAExC,QAAO;;AAGT,SAAS,iBAAiB,KAAyC;AACjE,KAAI,eAAe,YAAY,OAC7B,QAAO;AAET,QAAO,OAAO,UAAU,SAAS,KAAK,IAAI,KAAK;;AAGjD,SAAS,iBAAiB,QAA+C;AACvE,KAAI,kBAAkB,YAAY,OAChC,QAAO;AAET,QACE,OAAO,UAAU,SAAS,KAAK,OAAO,KAAK;;AAI/C,SAAS,gBAAgB,QAAgC;CACvD,MAAM,SAAS,gBAAgB,OAAO;AACtC,KAAI,OACF,QAAO;AAGT,OAAM,IAAI,WAAW,gBAAgB,qCAAqC;;AAG5E,SAAS,gBAAgB,QAA4C;AACnE,KAAI,UAAU,OAAO,WAAW,UAAU;EACxC,MAAM,QAAQ;AACd,MACE,iBAAiB,MAAM,OAAO,IAC9B,OAAO,MAAM,UAAU,cACvB,OAAO,MAAM,YAAY,cACzB,OAAO,MAAM,iBAAiB,cAC9B,OAAO,MAAM,gBAAgB,cAC7B,OAAO,MAAM,eAAe,cAC5B,OAAO,MAAM,eAAe,cAC5B,OAAO,MAAM,gBAAgB,WAE7B,QAAO"}
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@hot-updater/bsdiff",
3
- "version": "0.30.12",
3
+ "version": "0.31.0",
4
4
  "description": "Hot Updater BSdiff library that produces BSDIFF40 patches for RN bspatch flows",
5
+ "license": "MIT AND BSD-2-Clause AND bzip2-1.0.6",
5
6
  "type": "module",
6
7
  "main": "./dist/node.js",
7
8
  "types": "./dist/index.d.ts",
@@ -38,6 +39,7 @@
38
39
  }
39
40
  },
40
41
  "files": [
42
+ "NOTICE.md",
41
43
  "dist",
42
44
  "assets",
43
45
  "scripts/wasm-build",
@@ -2,7 +2,6 @@ use bzip2::read::BzDecoder;
2
2
  use bzip2::write::BzEncoder;
3
3
  use bzip2::Compression;
4
4
  use std::io::Cursor;
5
- use std::io::Read;
6
5
  use std::io::Write;
7
6
 
8
7
  const MIN_HEADER_LEN: u32 = 128;
@@ -100,7 +99,7 @@ pub unsafe extern "C" fn create_patch(
100
99
  Err(_) => return STATUS_INVALID_INPUT,
101
100
  };
102
101
 
103
- match generate_bsdiff40_patch(&base, &next) {
102
+ match generate_endsley_bsdiff43_patch(&base, &next) {
104
103
  Ok(patch) => {
105
104
  store_output(patch);
106
105
  STATUS_OK
@@ -125,7 +124,7 @@ pub unsafe extern "C" fn apply_patch(
125
124
  Err(_) => return STATUS_INVALID_INPUT,
126
125
  };
127
126
 
128
- match apply_bsdiff40_patch(&base, &patch) {
127
+ match apply_endsley_bsdiff43_patch(&base, &patch) {
129
128
  Ok(next) => {
130
129
  store_output(next);
131
130
  STATUS_OK
@@ -197,160 +196,48 @@ unsafe fn free_output_buffer() {
197
196
  LAST_OUTPUT_CAP = 0;
198
197
  }
199
198
 
200
- fn generate_bsdiff40_patch(old: &[u8], new: &[u8]) -> Result<Vec<u8>, String> {
199
+ fn generate_endsley_bsdiff43_patch(old: &[u8], new: &[u8]) -> Result<Vec<u8>, String> {
201
200
  let mut legacy_patch = Vec::new();
202
201
  bsdiff::diff(old, new, &mut legacy_patch).map_err(|error| format!("{error}"))?;
203
202
 
204
- let (ctrl_block, diff_block, extra_block) = split_legacy_patch(&legacy_patch)?;
203
+ let compressed_patch = bzip2_compress(&legacy_patch)?;
205
204
 
206
- let ctrl_bz = bzip2_compress(&ctrl_block)?;
207
- let diff_bz = bzip2_compress(&diff_block)?;
208
- let extra_bz = bzip2_compress(&extra_block)?;
209
-
210
- let mut patch = Vec::with_capacity(32 + ctrl_bz.len() + diff_bz.len() + extra_bz.len());
211
- patch.extend_from_slice(b"BSDIFF40");
212
- write_offt(ctrl_bz.len() as i64, &mut patch)?;
213
- write_offt(diff_bz.len() as i64, &mut patch)?;
205
+ let mut patch = Vec::with_capacity(24 + compressed_patch.len());
206
+ patch.extend_from_slice(b"ENDSLEY/BSDIFF43");
214
207
  write_offt(new.len() as i64, &mut patch)?;
215
- patch.extend_from_slice(&ctrl_bz);
216
- patch.extend_from_slice(&diff_bz);
217
- patch.extend_from_slice(&extra_bz);
208
+ patch.extend_from_slice(&compressed_patch);
218
209
 
219
210
  Ok(patch)
220
211
  }
221
212
 
222
- fn split_legacy_patch(legacy_patch: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), String> {
223
- let mut controls = Vec::new();
224
- let mut diff_data = Vec::new();
225
- let mut extra_data = Vec::new();
226
-
227
- let mut cursor = 0usize;
228
- while cursor < legacy_patch.len() {
229
- if legacy_patch.len() - cursor < 24 {
230
- return Err("invalid legacy patch control length".to_string());
231
- }
232
-
233
- let ctrl_bytes = &legacy_patch[cursor..cursor + 24];
234
- let add_len = read_offt(&ctrl_bytes[0..8])?;
235
- let copy_len = read_offt(&ctrl_bytes[8..16])?;
236
- let _seek = read_offt(&ctrl_bytes[16..24])?;
237
-
238
- if add_len < 0 || copy_len < 0 {
239
- return Err("invalid legacy patch control values".to_string());
240
- }
241
-
242
- let add_len = usize::try_from(add_len).map_err(|_| "add length overflow".to_string())?;
243
- let copy_len =
244
- usize::try_from(copy_len).map_err(|_| "copy length overflow".to_string())?;
245
- cursor += 24;
246
-
247
- if legacy_patch.len() - cursor < add_len {
248
- return Err("legacy patch truncated in diff section".to_string());
249
- }
250
- diff_data.extend_from_slice(&legacy_patch[cursor..cursor + add_len]);
251
- cursor += add_len;
252
-
253
- if legacy_patch.len() - cursor < copy_len {
254
- return Err("legacy patch truncated in extra section".to_string());
255
- }
256
- extra_data.extend_from_slice(&legacy_patch[cursor..cursor + copy_len]);
257
- cursor += copy_len;
258
-
259
- controls.extend_from_slice(ctrl_bytes);
213
+ fn apply_endsley_bsdiff43_patch(old: &[u8], patch: &[u8]) -> Result<Vec<u8>, String> {
214
+ if patch.len() < 24 || &patch[0..16] != b"ENDSLEY/BSDIFF43" {
215
+ return Err("invalid ENDSLEY/BSDIFF43 header".to_string());
260
216
  }
261
217
 
262
- Ok((controls, diff_data, extra_data))
263
- }
264
-
265
- fn apply_bsdiff40_patch(old: &[u8], patch: &[u8]) -> Result<Vec<u8>, String> {
266
- if patch.len() < 32 || &patch[0..8] != b"BSDIFF40" {
267
- return Err("invalid BSDIFF40 header".to_string());
218
+ let new_size = read_offt(&patch[16..24])?;
219
+ if new_size < 0 {
220
+ return Err("negative ENDSLEY/BSDIFF43 target size".to_string());
268
221
  }
269
222
 
270
- let ctrl_len = read_offt(&patch[8..16])?;
271
- let diff_len = read_offt(&patch[16..24])?;
272
- let new_size = read_offt(&patch[24..32])?;
273
- if ctrl_len < 0 || diff_len < 0 || new_size < 0 {
274
- return Err("negative BSDIFF40 header values".to_string());
275
- }
276
-
277
- let ctrl_len = usize::try_from(ctrl_len).map_err(|_| "control length overflow".to_string())?;
278
- let diff_len = usize::try_from(diff_len).map_err(|_| "diff length overflow".to_string())?;
279
223
  let new_size = usize::try_from(new_size).map_err(|_| "new size overflow".to_string())?;
224
+ let mut patch_reader = BzDecoder::new(Cursor::new(&patch[24..]));
225
+ let mut output = Vec::with_capacity(new_size);
280
226
 
281
- let ctrl_start = 32usize;
282
- let ctrl_end = ctrl_start
283
- .checked_add(ctrl_len)
284
- .ok_or_else(|| "control block overflow".to_string())?;
285
- let diff_end = ctrl_end
286
- .checked_add(diff_len)
287
- .ok_or_else(|| "diff block overflow".to_string())?;
288
- if diff_end > patch.len() {
289
- return Err("BSDIFF40 block bounds are invalid".to_string());
290
- }
291
-
292
- let mut ctrl_reader = BzDecoder::new(Cursor::new(&patch[ctrl_start..ctrl_end]));
293
- let mut diff_reader = BzDecoder::new(Cursor::new(&patch[ctrl_end..diff_end]));
294
- let mut extra_reader = BzDecoder::new(Cursor::new(&patch[diff_end..]));
295
-
296
- let mut out = Vec::with_capacity(new_size);
297
- let mut old_pos: i64 = 0;
298
-
299
- while out.len() < new_size {
300
- let mut ctrl_buf = [0u8; 24];
301
- ctrl_reader
302
- .read_exact(&mut ctrl_buf)
303
- .map_err(|error| format!("failed to read control block: {error}"))?;
304
-
305
- let add_len = read_offt(&ctrl_buf[0..8])?;
306
- let copy_len = read_offt(&ctrl_buf[8..16])?;
307
- let seek_len = read_offt(&ctrl_buf[16..24])?;
308
- if add_len < 0 || copy_len < 0 {
309
- return Err("negative add/copy length in control block".to_string());
310
- }
311
-
312
- let add_len = usize::try_from(add_len).map_err(|_| "add length overflow".to_string())?;
313
- let copy_len =
314
- usize::try_from(copy_len).map_err(|_| "copy length overflow".to_string())?;
315
-
316
- let mut diff_bytes = vec![0u8; add_len];
317
- diff_reader
318
- .read_exact(&mut diff_bytes)
319
- .map_err(|error| format!("failed to read diff block: {error}"))?;
320
- for delta_byte in diff_bytes {
321
- if old_pos < 0 || old_pos as usize >= old.len() {
322
- return Err("old file offset out of bounds".to_string());
323
- }
324
- let old_byte = old[old_pos as usize];
325
- out.push(delta_byte.wrapping_add(old_byte));
326
- old_pos += 1;
327
- }
328
-
329
- let mut extra_bytes = vec![0u8; copy_len];
330
- extra_reader
331
- .read_exact(&mut extra_bytes)
332
- .map_err(|error| format!("failed to read extra block: {error}"))?;
333
- out.extend_from_slice(&extra_bytes);
334
-
335
- old_pos = old_pos
336
- .checked_add(seek_len)
337
- .ok_or_else(|| "old file seek overflow".to_string())?;
338
-
339
- if out.len() > new_size {
340
- return Err("patch output exceeds target size".to_string());
341
- }
342
- }
227
+ bsdiff::patch(old, &mut patch_reader, &mut output).map_err(|error| format!("{error}"))?;
343
228
 
344
- if out.len() != new_size {
229
+ if output.len() != new_size {
345
230
  return Err("patch output length mismatch".to_string());
346
231
  }
347
232
 
348
- Ok(out)
233
+ Ok(output)
349
234
  }
350
235
 
351
236
  fn bzip2_compress(input: &[u8]) -> Result<Vec<u8>, String> {
352
237
  let mut encoder = BzEncoder::new(Vec::new(), Compression::new(6));
353
- encoder.write_all(input).map_err(|error| format!("{error}"))?;
238
+ encoder
239
+ .write_all(input)
240
+ .map_err(|error| format!("{error}"))?;
354
241
  encoder.finish().map_err(|error| format!("{error}"))
355
242
  }
356
243