├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── edit_warning.txt ├── strip_xdelta3_decoder.sh ├── tests ├── debug.js ├── misc.js ├── style.css ├── testA.html ├── testA │ ├── A.delta │ ├── A.expectedTarget │ └── A.source ├── testB.html ├── testB │ ├── B.delta │ └── B.expectedTarget ├── testC.html ├── testC │ ├── C.delta │ └── C.expectedTarget ├── testD.html ├── testD │ ├── D.delta │ ├── D.expectedTarget │ └── D.source ├── testE.html └── testE │ ├── E.delta │ ├── E.expectedTarget │ └── E.source ├── xdelta3-decode_with_debug.h ├── xdelta3_decoder.js ├── xdelta3_decoder_with_debug.js └── xdelta3_with_debug.c /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution, 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult [GitHub Help] for more 22 | information on using pull requests. 23 | 24 | [GitHub Help]: https://help.github.com/articles/about-pull-requests/ 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This code is in a very rough state. 2 | 3 | This is a Javascript port of XDelta3 C++ decoder. 4 | See the XDelta3 website for infomation on XDelta3: http://xdelta.org/ 5 | 6 | XDelta3 is written in C and can compress, make diffs, and decode (re-expand) 7 | binary as well as text files. As a file compressor XDelta3 deltas are slightly 8 | larger than the same file compressed with gzip. However, depending on the data, 9 | XDelta3 diffs can be dramatically smaller than the compressed files. 10 | 11 | This port to Javascript only implements the decoder. To use this Javacript code 12 | use the XDelta3 C code to diff or compress files and use this Javascript code to 13 | decode the files in the browser. 14 | 15 | This port was done by adding print statements to the C code and then making the 16 | Javascript mirror the C code. When decoding files the outputs of the C and 17 | Javascript code should be identical. Meld is a useful tool for comparing the 2 18 | print outputs. The debug\_save() routine can be used to save the Javascript 19 | print outputs without the console line numbers. 20 | 21 | To make changes to this code: 22 | 23 | 1. compile the XDelta C code using the C files with the print statements 24 | 1. decode a test file using the C code and save the print statements in a file 25 | 1. modify the xdelta3\_decoder\_with\_debug.js 26 | 1. decode the same test file in the browser 27 | 1. copy the dev console log to a file 28 | 1. verify that the C and Javascript print statements are identical 29 | 30 | This Javascript has be tested on smaller (300KB) files. 31 | 32 | -------------------------------------------------------------------------------- /edit_warning.txt: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////// 2 | // // 3 | // DO NOT EDIT THIS FILE! // 4 | // // 5 | // THIS FILE WILL BE OVERWRITTEN DURING THE BUILD PROCESS! // 6 | // // 7 | // EDIT xdelta3_decoder_with_debug.js // 8 | // // 9 | // THIS FILE IS GENERATED FILE BY REMOVING THE DEBUG // 10 | // COMMENTS FROM xdelta3_decoder_with_debug.js // 11 | // // 12 | ///////////////////////////////////////////////////////////// 13 | -------------------------------------------------------------------------------- /strip_xdelta3_decoder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cat edit_warning.txt > xdelta3_decoder.js 3 | grep -v '// DEBUG ONLY' xdelta3_decoder_with_debug.js >> xdelta3_decoder.js 4 | -------------------------------------------------------------------------------- /tests/debug.js: -------------------------------------------------------------------------------- 1 | 2 | var debug_text = ''; 3 | 4 | function printf(text) { 5 | var last = text.length - 1; 6 | if (text[last] == '\n') { 7 | //console.log(text.substr(0,last)); 8 | console.log(text); 9 | } else { 10 | console.log(text); 11 | } 12 | debug_text += text; 13 | } 14 | 15 | function debug_save(){ 16 | var filename = 'debug.log'; 17 | 18 | var blob = new Blob([debug_text], {type: 'text/json'}), 19 | e = document.createEvent('MouseEvents'), 20 | a = document.createElement('a') 21 | 22 | a.download = filename 23 | a.href = window.URL.createObjectURL(blob) 24 | a.dataset.downloadurl = ['text/json', a.download, a.href].join(':') 25 | e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null) 26 | a.dispatchEvent(e) 27 | 28 | debug_text = ''; 29 | } 30 | 31 | function dumpBytes(bytes, offset, length) { 32 | printf('++++++++++++++++++++++++++++++++++++++++++\n'); 33 | var output = ''; 34 | var text = ''; 35 | for (var i = 0; i < length; i++) { 36 | var aByte = bytes[offset + i]; 37 | var dec = ' ' + aByte; 38 | if (aByte >= 32 && aByte < 127) { 39 | text += String.fromCharCode(aByte); 40 | } else { 41 | text += ' '; 42 | } 43 | output += dec.substr(-3) + ', '; 44 | if (i % 8 == 7) { 45 | output += ' // \'' + text + '\'\n'; 46 | text = ''; 47 | } 48 | } 49 | i--; 50 | if (i % 8 != 7) { 51 | output += ' // \'' + text + '\'\n'; 52 | } 53 | printf(output); 54 | printf('++++++++++++++++++++++++++++++++++++++++++\n'); 55 | } 56 | 57 | /** 58 | * @param {!xd3_hinst} inst1 59 | * @param {!xd3_hinst} inst2 60 | */ 61 | function printInstructionPair(inst1, inst2) { 62 | printf( 63 | typeToTypeString(inst1.type) + '/' + inst1.size + '/' + typeToMode(inst1.type) + ' : ' + 64 | typeToTypeString(inst2.type) + '/' + inst2.size + '/' + typeToMode(inst2.type) + "\n"); 65 | } 66 | 67 | function dumpCodeTableRows(tableRows, startRow, endRow) { 68 | var output = ''; 69 | output += '==============================================\n'; 70 | output += 'code table:\n'; 71 | for (var row = startRow; row < endRow; row++) { 72 | var tableRow = tableRows[row]; 73 | output += rightJustify(row, 3) + ': '; 74 | output += typeToTypeString(tableRow.type1) + '(' + tableRow.type1 + '), '; 75 | output += rightJustify(tableRow.size1, 2) + ', '; 76 | output += typeToMode(tableRow.type1) + ', '; 77 | output += typeToTypeString(tableRow.type2) + '(' + tableRow.type2 + '), '; 78 | output += rightJustify(tableRow.size2, 2) + ', '; 79 | output += typeToMode(tableRow.type2); 80 | output += '\n'; 81 | } 82 | output += '==============================================\n'; 83 | printf(output); 84 | } 85 | 86 | // TODO(bstell): move to a common file 87 | var XD3_NOOP = 0; 88 | var XD3_ADD = 1; 89 | var XD3_RUN = 2; 90 | var XD3_CPY = 3; 91 | 92 | function typeToMode(type) { 93 | if (type == XD3_NOOP) { 94 | return 0; 95 | } 96 | else if (type == XD3_ADD) { 97 | return 0; 98 | } 99 | else if (type == XD3_RUN) { 100 | return 0; 101 | } 102 | else { 103 | return type - XD3_CPY; 104 | } 105 | } 106 | 107 | function typeToTypeString(type) { 108 | var typeString; 109 | if (type == XD3_NOOP) { 110 | typeString = 'XD3_NOOP'; 111 | } 112 | else if (type == XD3_ADD) { 113 | typeString = 'XD3_ADD'; 114 | } 115 | else if (type == XD3_RUN) { 116 | typeString = 'XD3_RUN'; 117 | } 118 | else { 119 | typeString = 'XD3_CPY'; 120 | } 121 | return typeString; 122 | } 123 | 124 | function rightJustify(str, len) { 125 | var longStr = ' ' + str; 126 | return longStr.substr(-len); 127 | } 128 | 129 | function toHexStr(val) { 130 | var hex = '0' + val.toString(16); 131 | return '0x' + hex.substr(-2); 132 | } 133 | 134 | -------------------------------------------------------------------------------- /tests/misc.js: -------------------------------------------------------------------------------- 1 | 2 | function setInnerHtml(id, message) { 3 | var msgEle = document.getElementById(id); 4 | msgEle.innerHTML = message; 5 | console.log(message); 6 | } 7 | 8 | function compareBytes(bytesA, bytesB) { 9 | var msg; 10 | var length = bytesB.byteLength; 11 | for (var i = 0; i < length; i++) { 12 | if (bytesA[i] != bytesB[i]) { 13 | return i +': bytesA('+bytesA[i]+') != bytesB('+bytesB[i]+')'; 14 | } 15 | } 16 | return 'matched!'; 17 | } 18 | 19 | // Load a file. 20 | function loadFile(url, callback) { 21 | var xhttp = new XMLHttpRequest(); 22 | xhttp.onreadystatechange = function() { 23 | if (this.readyState == 4) { 24 | if (this.status == 200) { 25 | callback(new Uint8Array(this.response)); 26 | } 27 | } 28 | }; 29 | xhttp.open("GET", url, true); 30 | xhttp.responseType = "arraybuffer"; 31 | xhttp.send(); 32 | } 33 | 34 | function addRow(id, name, path) { 35 | var msgEle = document.getElementById(id); 36 | msgEle.innerHTML += '' + name + ':' + path + ''; 37 | } 38 | -------------------------------------------------------------------------------- /tests/style.css: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | } 4 | td, th { 5 | border: 1px solid lightGray; 6 | } 7 | -------------------------------------------------------------------------------- /tests/testA.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | XDelta3 TestA: text with source 5 | 6 | 7 | 8 | 9 | 50 | 51 | 52 | XDelta3 decode text delta with a source

53 | 54 | status:

55 |
56 | 61 | 62 | -------------------------------------------------------------------------------- /tests/testA/A.delta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testA/A.delta -------------------------------------------------------------------------------- /tests/testA/A.expectedTarget: -------------------------------------------------------------------------------- 1 | /** 2 | * Enum for error values. 3 | * @enum {string} 4 | */ 5 | tachyfont.Error = { 6 | FILE_ID: 'ETF', 7 | // 02-03 no longer used. 8 | KNOWN_WINDOW_ON_ERROR: '05', 9 | UNKNOWN_WINDOW_ON_ERROR: '06', 10 | NOT_ENOUGH_STORAGE: '07', 11 | STORAGE_INFORMATION_FUNCTION: '08', 12 | GET_STORAGE_INFORMATION: '09', 13 | NO_PRELUDE_REPORTS: '10', 14 | PRELUDE_REPORT_TYPE: '11', 15 | BELOW_GLOBAL_STABLE_TIME: '12', 16 | OPEN_GLOBAL_DATABASE: '13', 17 | NO_INDEXED_DB: '14', 18 | NO_MUTATION_OBSERVER: '15', 19 | NO_FONT_LOADER: '16', 20 | PAGE_LOADED: '17', 21 | GET_COMPACT_FONT: '18', 22 | // 19 no longer used. 23 | DISPLAY_COMPACT_FONT: '20', 24 | NO_UINT8ARRAY_FROM: '21', 25 | END: '00' 26 | }; 27 | 28 | 29 | /** 30 | * The error reporter for this file. 31 | * @param {string} errNum The error number (encoded in a string); 32 | * @param {*=} opt_errInfo Optional error object; 33 | * @param {string} opt_fontId Optional identifier for the font. 34 | */ 35 | tachyfont.reportError = function(errNum, opt_errInfo, opt_fontId) { 36 | var errInfo = opt_errInfo || ''; 37 | var fontId = opt_fontId || '000'; 38 | tachyfont.Reporter.reportError( 39 | tachyfont.Error.FILE_ID + errNum, fontId, errInfo); 40 | }; 41 | 42 | 43 | if (window.addEventListener) { 44 | /** 45 | * Report any uncaught errors. 46 | * @param {!Event} error The error information. 47 | * @private 48 | */ 49 | tachyfont.windowOnError_ = function(error) { 50 | if (!error['filename']) { 51 | // The information is stripped from the report because of CORS issues. 52 | tachyfont.reportError(tachyfont.Error.UNKNOWN_WINDOW_ON_ERROR); 53 | return; 54 | } 55 | var errorObj = {}; 56 | errorObj['message'] = error['message']; 57 | errorObj['filename'] = error['filename']; 58 | errorObj['lineno'] = error['lineno']; 59 | errorObj['colno'] = error['colno']; 60 | if (error.error) { 61 | errorObj['stack'] = error['error']['stack'].substring(0, 1000); 62 | } 63 | var errorStr = JSON.stringify(errorObj); 64 | tachyfont.reportError(tachyfont.Error.KNOWN_WINDOW_ON_ERROR, errorStr); 65 | }; 66 | window.addEventListener('error', tachyfont.windowOnError_, false); 67 | } 68 | -------------------------------------------------------------------------------- /tests/testA/A.source: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @license 5 | * Copyright 2015 Google Inc. All rights reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | * use this file except in compliance with the License. You may obtain a copy of 9 | * the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | * License for the specific language governing permissions and limitations under 17 | * the License. 18 | */ 19 | 20 | goog.provide('tachyfont'); 21 | goog.provide('tachyfont.Error'); 22 | goog.provide('tachyfont.TachyFont'); 23 | 24 | goog.require('goog.Promise'); 25 | goog.require('goog.Uri'); 26 | goog.require('goog.asserts'); 27 | goog.require('goog.debug.Logger'); 28 | goog.require('tachyfont.Define'); 29 | /** @suppress {extraRequire} */ 30 | goog.require('tachyfont.FontsInfo'); 31 | goog.require('tachyfont.IncrementalFont'); 32 | goog.require('tachyfont.Persist'); 33 | goog.require('tachyfont.Reporter'); 34 | goog.require('tachyfont.TachyFontSet'); 35 | goog.require('tachyfont.log'); 36 | goog.require('tachyfont.utils'); 37 | 38 | 39 | 40 | /** 41 | * TachyFont - A namespace. 42 | * @param {!tachyfont.FontInfo} fontInfo The font info. 43 | * @param {boolean} dropData If true then drop the persistent store data. 44 | * @param {!Object=} opt_params Optional parameters. 45 | * @constructor 46 | */ 47 | tachyfont.TachyFont = function(fontInfo, dropData, opt_params) { 48 | var params = opt_params || {}; 49 | 50 | /** 51 | * The object that handles the binary manipulation of the font data. 52 | * @private {!tachyfont.IncrementalFont.obj} 53 | * TODO(bstell): integrate the manager into this object. 54 | */ 55 | this.incrfont_ = tachyfont.IncrementalFont.createManager(fontInfo, dropData, 56 | params); 57 | }; 58 | 59 | 60 | /** 61 | * Lazily load the data for these chars. 62 | */ 63 | tachyfont.TachyFont.prototype.getIncrfont = function() { 64 | return this.incrfont_; 65 | }; 66 | 67 | 68 | /** 69 | * Lazily load the data for these chars. 70 | */ 71 | tachyfont.TachyFont.prototype.loadNeededChars = function() { 72 | this.incrfont_.loadChars(); 73 | }; 74 | 75 | 76 | /** 77 | * The persistence 'stable' time. 78 | * If the data has been in persistent store longer than this then the data is 79 | * considered to be stable; ie: not being automatically cleared. The time is in 80 | * milliseconds. 81 | * @type {number} 82 | */ 83 | tachyfont.TachyFont.GLOBAL_STABLE_DATA_TIME = 24 * 60 * 60 * 1000; 84 | 85 | 86 | /** 87 | * Enum for error values. 88 | * @enum {string} 89 | */ 90 | tachyfont.Error = { 91 | FILE_ID: 'ETF', 92 | // 02-03 no longer used. 93 | KNOWN_WINDOW_ON_ERROR: '05', 94 | UNKNOWN_WINDOW_ON_ERROR: '06', 95 | NOT_ENOUGH_STORAGE: '07', 96 | STORAGE_INFORMATION_FUNCTION: '08', 97 | GET_STORAGE_INFORMATION: '09', 98 | NO_PRELUDE_REPORTS: '10', 99 | PRELUDE_REPORT_TYPE: '11', 100 | BELOW_GLOBAL_STABLE_TIME: '12', 101 | OPEN_GLOBAL_DATABASE: '13', 102 | NO_INDEXED_DB: '14', 103 | NO_MUTATION_OBSERVER: '15', 104 | NO_FONT_LOADER: '16', 105 | PAGE_LOADED: '17', 106 | GET_COMPACT_FONT: '18', 107 | // 19 no longer used. 108 | DISPLAY_COMPACT_FONT: '20', 109 | NO_UINT8ARRAY_FROM: '21', 110 | END: '00' 111 | }; 112 | -------------------------------------------------------------------------------- /tests/testB.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | XDelta3 TestB: text/no-source 5 | 6 | 7 | 8 | 9 | 45 | 46 | 47 | XDelta3 decode text delta (no source)

48 | status:

49 |
50 | 55 | 56 | -------------------------------------------------------------------------------- /tests/testB/B.delta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testB/B.delta -------------------------------------------------------------------------------- /tests/testB/B.expectedTarget: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | goog.provide('tachyfont'); 4 | goog.provide('tachyfont.Error'); 5 | goog.provide('tachyfont.TachyFont'); 6 | 7 | goog.require('goog.Promise'); 8 | goog.require('goog.Uri'); 9 | goog.require('goog.asserts'); 10 | goog.require('goog.debug.Logger'); 11 | goog.require('tachyfont.Define'); 12 | /** @suppress {extraRequire} */ 13 | goog.require('tachyfont.FontsInfo'); 14 | goog.require('tachyfont.IncrementalFont'); 15 | goog.require('tachyfont.Persist'); 16 | goog.require('tachyfont.Reporter'); 17 | goog.require('tachyfont.TachyFontSet'); 18 | goog.require('tachyfont.log'); 19 | goog.require('tachyfont.utils'); 20 | 21 | 22 | 23 | /** 24 | * TachyFont - A namespace. 25 | * @param {!tachyfont.FontInfo} fontInfo The font info. 26 | * @param {boolean} dropData If true then drop the persistent store data. 27 | * @param {!Object=} opt_params Optional parameters. 28 | * @constructor 29 | */ 30 | tachyfont.TachyFont = function(fontInfo, dropData, opt_params) { 31 | var params = opt_params || {}; 32 | 33 | /** 34 | * The object that handles the binary manipulation of the font data. 35 | * @private {!tachyfont.IncrementalFont.obj} 36 | * TODO(bstell): integrate the manager into this object. 37 | */ 38 | this.incrfont_ = tachyfont.IncrementalFont.createManager(fontInfo, dropData, 39 | params); 40 | }; 41 | 42 | 43 | /** 44 | * Lazily load the data for these chars. 45 | */ 46 | tachyfont.TachyFont.prototype.getIncrfont = function() { 47 | return this.incrfont_; 48 | }; 49 | 50 | 51 | /** 52 | * Lazily load the data for these chars. 53 | */ 54 | tachyfont.TachyFont.prototype.loadNeededChars = function() { 55 | this.incrfont_.loadChars(); 56 | }; 57 | 58 | 59 | /** 60 | * The persistence 'stable' time. 61 | * If the data has been in persistent store longer than this then the data is 62 | * considered to be stable; ie: not being automatically cleared. The time is in 63 | * milliseconds. 64 | * @type {number} 65 | */ 66 | tachyfont.TachyFont.GLOBAL_STABLE_DATA_TIME = 24 * 60 * 60 * 1000; 67 | 68 | 69 | /** 70 | * Enum for error values. 71 | * @enum {string} 72 | */ 73 | tachyfont.Error = { 74 | FILE_ID: 'ETF', 75 | // 02-03 no longer used. 76 | KNOWN_WINDOW_ON_ERROR: '05', 77 | UNKNOWN_WINDOW_ON_ERROR: '06', 78 | NOT_ENOUGH_STORAGE: '07', 79 | STORAGE_INFORMATION_FUNCTION: '08', 80 | GET_STORAGE_INFORMATION: '09', 81 | NO_PRELUDE_REPORTS: '10', 82 | PRELUDE_REPORT_TYPE: '11', 83 | BELOW_GLOBAL_STABLE_TIME: '12', 84 | OPEN_GLOBAL_DATABASE: '13', 85 | NO_INDEXED_DB: '14', 86 | NO_MUTATION_OBSERVER: '15', 87 | NO_FONT_LOADER: '16', 88 | PAGE_LOADED: '17', 89 | GET_COMPACT_FONT: '18', 90 | // 19 no longer used. 91 | DISPLAY_COMPACT_FONT: '20', 92 | NO_UINT8ARRAY_FROM: '21', 93 | END: '00' 94 | }; 95 | 96 | 97 | -------------------------------------------------------------------------------- /tests/testC.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | XDelta3 TestC: binary/no-source 5 | 6 | 7 | 8 | 9 | 45 | 46 | 47 | XDelta3 decode binary delta (no source)

48 | status:

49 |
50 | 55 | 56 | -------------------------------------------------------------------------------- /tests/testC/C.delta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testC/C.delta -------------------------------------------------------------------------------- /tests/testC/C.expectedTarget: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testC/C.expectedTarget -------------------------------------------------------------------------------- /tests/testD.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | XDelta3 TestD: big binary with source 5 | 6 | 7 | 8 | 9 | 50 | 51 | 52 | XDelta3 decode big binary delta with a source

53 | 54 | status:

55 |
56 | 61 | 62 | -------------------------------------------------------------------------------- /tests/testD/D.delta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testD/D.delta -------------------------------------------------------------------------------- /tests/testD/D.expectedTarget: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testD/D.expectedTarget -------------------------------------------------------------------------------- /tests/testD/D.source: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testD/D.source -------------------------------------------------------------------------------- /tests/testE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | XDelta3 TestE: big binary with source and secondary compressor 5 | 6 | 7 | 8 | 9 | 50 | 51 | 52 | XDelta3 decode big binary delta with a source and secondary compressor

53 | 54 | status:

55 |
56 | 61 | 62 | -------------------------------------------------------------------------------- /tests/testE/E.delta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testE/E.delta -------------------------------------------------------------------------------- /tests/testE/E.expectedTarget: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testE/E.expectedTarget -------------------------------------------------------------------------------- /tests/testE/E.source: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/xdelta3-decoder-js/065bdca8991bb4610aeb158f126321d471c40631/tests/testE/E.source -------------------------------------------------------------------------------- /xdelta3-decode_with_debug.h: -------------------------------------------------------------------------------- 1 | /* xdelta3 - delta compression tools and library 2 | Copyright 2016 Joshua MacDonald 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef _XDELTA3_DECODE_H_ 18 | #define _XDELTA3_DECODE_H_ 19 | 20 | #include "xdelta3-internal.h" 21 | 22 | #define SRCORTGT(x) ((((x) & VCD_SRCORTGT) == VCD_SOURCE) ? \ 23 | VCD_SOURCE : ((((x) & VCD_SRCORTGT) == \ 24 | VCD_TARGET) ? VCD_TARGET : 0)) 25 | 26 | static inline int 27 | xd3_decode_byte (xd3_stream *stream, usize_t *val) 28 | { 29 | //printf("xd3_decode_byte: pos = %d, ", stream->total_in); 30 | if (stream->avail_in == 0) 31 | { 32 | printf("no more data\n"); 33 | stream->msg = "further input required"; 34 | return XD3_INPUT; 35 | } 36 | 37 | //printf("value = %d(0x%x)\n", stream->next_in[0]); 38 | (*val) = stream->next_in[0]; 39 | 40 | DECODE_INPUT (1); 41 | return 0; 42 | } 43 | 44 | static inline int 45 | xd3_decode_bytes (xd3_stream *stream, uint8_t *buf, usize_t *pos, usize_t size) 46 | { 47 | usize_t want; 48 | usize_t take; 49 | //printf("xd3_decode_bytes: pos = %d\n", *pos); 50 | 51 | /* Note: The case where (*pos == size) happens when a zero-length 52 | * appheader or code table is transmitted, but there is nothing in 53 | * the standard against that. */ 54 | while (*pos < size) 55 | { 56 | if (stream->avail_in == 0) 57 | { 58 | stream->msg = "further input required"; 59 | return XD3_INPUT; 60 | } 61 | 62 | want = size - *pos; 63 | take = xd3_min (want, stream->avail_in); 64 | 65 | memcpy (buf + *pos, stream->next_in, (size_t) take); 66 | 67 | DECODE_INPUT (take); 68 | (*pos) += take; 69 | } 70 | 71 | return 0; 72 | } 73 | 74 | /* Initialize the decoder for a new window. The dec_tgtlen value is 75 | * preserved across successive window decodings, and the update to 76 | * dec_winstart is delayed until a new window actually starts. This 77 | * is to avoid throwing an error due to overflow until the last 78 | * possible moment. This makes it possible to encode exactly 4GB 79 | * through a 32-bit encoder. */ 80 | static int 81 | xd3_decode_init_window (xd3_stream *stream) 82 | { 83 | stream->dec_cpylen = 0; 84 | stream->dec_cpyoff = 0; 85 | stream->dec_cksumbytes = 0; 86 | 87 | xd3_init_cache (& stream->acache); 88 | 89 | return 0; 90 | } 91 | 92 | /* Allocates buffer space for the target window and possibly the 93 | * VCD_TARGET copy-window. Also sets the base of the two copy 94 | * segments. */ 95 | static int 96 | xd3_decode_setup_buffers (xd3_stream *stream) 97 | { 98 | printf("xd3_decode_setup_buffers\n"); 99 | /* If VCD_TARGET is set then the previous buffer may be reused. */ 100 | if (stream->dec_win_ind & VCD_TARGET) 101 | { 102 | printf("xd3_decode_setup_buffers: VCD_TARGET set\n"); 103 | /* Note: this implementation is untested, since Xdelta3 itself 104 | * does not implement an encoder for VCD_TARGET mode. Thus, mark 105 | * unimplemented until needed. */ 106 | if (1) 107 | { 108 | stream->msg = "VCD_TARGET not implemented"; 109 | return XD3_UNIMPLEMENTED; 110 | } 111 | 112 | /* But this implementation only supports copying from the last 113 | * target window. If the offset is outside that range, it can't 114 | * be done. */ 115 | if (stream->dec_cpyoff < stream->dec_laststart) 116 | { 117 | stream->msg = "unsupported VCD_TARGET offset"; 118 | return XD3_INVALID_INPUT; 119 | } 120 | 121 | /* See if the two windows are the same. This indicates the 122 | * first time VCD_TARGET is used. This causes a second buffer 123 | * to be allocated, after that the two are swapped in the 124 | * DEC_FINISH case. */ 125 | if (stream->dec_lastwin == stream->next_out) 126 | { 127 | stream->next_out = NULL; 128 | stream->space_out = 0; 129 | } 130 | 131 | /* TODO: (See note above, this looks incorrect) */ 132 | stream->dec_cpyaddrbase = stream->dec_lastwin + 133 | (usize_t) (stream->dec_cpyoff - stream->dec_laststart); 134 | } 135 | 136 | /* See if the current output window is large enough. */ 137 | //printf("stream->space_out = %d\n", stream->space_out); 138 | //printf("stream->dec_tgtlen = %d\n", stream->dec_tgtlen); 139 | if (stream->space_out < stream->dec_tgtlen) 140 | { 141 | xd3_free (stream, stream->dec_buffer); 142 | 143 | stream->space_out = 144 | xd3_round_blksize (stream->dec_tgtlen, XD3_ALLOCSIZE); 145 | //printf("stream->space_out = %d\n", stream->space_out); 146 | 147 | if ((stream->dec_buffer = 148 | (uint8_t*) xd3_alloc (stream, stream->space_out, 1)) == NULL) 149 | { 150 | return ENOMEM; 151 | } 152 | 153 | stream->next_out = stream->dec_buffer; 154 | } 155 | 156 | /* dec_tgtaddrbase refers to an invalid base address, but it is 157 | * always used with a sufficiently large instruction offset (i.e., 158 | * beyond the copy window). This condition is enforced by 159 | * xd3_decode_output_halfinst. */ 160 | stream->dec_tgtaddrbase = stream->next_out - stream->dec_cpylen; 161 | //printf("stream->next_out = %d\n", stream->next_out); 162 | //printf("stream->dec_cpylen = %d\n", stream->dec_cpylen); 163 | //printf("stream->dec_tgtaddrbase = %d\n", stream->dec_tgtaddrbase); 164 | //printf("stream->dec_tgtaddrbase_delta = %d\n", stream->dec_tgtaddrbase - stream->dec_buffer); 165 | 166 | return 0; 167 | } 168 | 169 | static int 170 | xd3_decode_allocate (xd3_stream *stream, 171 | usize_t size, 172 | uint8_t **buf_ptr, 173 | usize_t *buf_alloc) 174 | { 175 | IF_DEBUG2 (DP(RINT "[xd3_decode_allocate] size %"W"u alloc %"W"u\n", 176 | size, *buf_alloc)); 177 | 178 | if (*buf_ptr != NULL && *buf_alloc < size) 179 | { 180 | xd3_free (stream, *buf_ptr); 181 | *buf_ptr = NULL; 182 | } 183 | 184 | if (*buf_ptr == NULL) 185 | { 186 | *buf_alloc = xd3_round_blksize (size, XD3_ALLOCSIZE); 187 | 188 | if ((*buf_ptr = (uint8_t*) xd3_alloc (stream, *buf_alloc, 1)) == NULL) 189 | { 190 | return ENOMEM; 191 | } 192 | } 193 | 194 | return 0; 195 | } 196 | 197 | static int 198 | xd3_decode_section (xd3_stream *stream, 199 | xd3_desect *section, 200 | xd3_decode_state nstate, 201 | int copy) 202 | { 203 | XD3_ASSERT (section->pos <= section->size); 204 | XD3_ASSERT (stream->dec_state != nstate); 205 | 206 | if (section->pos < section->size) 207 | { 208 | usize_t sect_take; 209 | 210 | if (stream->avail_in == 0) 211 | { 212 | return XD3_INPUT; 213 | } 214 | 215 | if ((copy == 0) && (section->pos == 0)) 216 | { 217 | /* No allocation/copy needed */ 218 | //printf("No allocation/copy needed\n"); 219 | section->buf = stream->next_in; 220 | sect_take = section->size; 221 | //printf(": sect_take = %d (%s/%d)\n", sect_take, __FILE__, __LINE__); 222 | //printf("++++++++++++++++++++++\n"); 223 | //for (unsigned int i = 0; i < sect_take; i++) { 224 | // printf("0x%02x, ", *(section->buf + i)); 225 | // if (i % 8 == 7) { 226 | // printf("\n"); 227 | // } 228 | //} 229 | //printf("\n"); 230 | //printf("++++++++++++++++++++++\n"); 231 | IF_DEBUG1 (DP(RINT "[xd3_decode_section] zerocopy %"W"u @ %"W"u avail %"W"u\n", 232 | sect_take, section->pos, stream->avail_in)); 233 | } 234 | else 235 | { 236 | printf("allocation/copy is needed\n"); 237 | usize_t sect_need = section->size - section->pos; 238 | printf("sect_needed = %d\n", sect_need); 239 | 240 | /* Allocate and copy */ 241 | sect_take = xd3_min (sect_need, stream->avail_in); 242 | 243 | if (section->pos == 0) 244 | { 245 | int ret; 246 | 247 | if ((ret = xd3_decode_allocate (stream, 248 | section->size, 249 | & section->copied1, 250 | & section->alloc1))) 251 | { 252 | return ret; 253 | } 254 | 255 | section->buf = section->copied1; 256 | } 257 | 258 | IF_DEBUG2 (DP(RINT "[xd3_decode_section] take %"W"u @ %"W"u [need %"W"u] avail %"W"u\n", 259 | sect_take, section->pos, sect_need, stream->avail_in)); 260 | XD3_ASSERT (section->pos + sect_take <= section->alloc1); 261 | 262 | memcpy (section->copied1 + section->pos, 263 | stream->next_in, 264 | sect_take); 265 | } 266 | 267 | section->pos += sect_take; 268 | 269 | stream->dec_winbytes += sect_take; 270 | 271 | DECODE_INPUT (sect_take); 272 | } 273 | 274 | if (section->pos < section->size) 275 | { 276 | printf(": = (%s/%d)\n", __FILE__, __LINE__); 277 | IF_DEBUG1 (DP(RINT "[xd3_decode_section] further input required %"W"u\n", 278 | section->size - section->pos)); 279 | stream->msg = "further input required"; 280 | return XD3_INPUT; 281 | } 282 | 283 | XD3_ASSERT (section->pos == section->size); 284 | 285 | //printf(": = (%s/%d)\n", __FILE__, __LINE__); 286 | stream->dec_state = nstate; 287 | section->buf_max = section->buf + section->size; 288 | section->pos = 0; 289 | return 0; 290 | } 291 | 292 | /* Decode the size and address for half of an instruction (i.e., a 293 | * single opcode). This updates the stream->dec_position, which are 294 | * bytes already output prior to processing this instruction. Perform 295 | * bounds checking for sizes and copy addresses, which uses the 296 | * dec_position (which is why these checks are done here). */ 297 | static int 298 | xd3_decode_parse_halfinst (xd3_stream *stream, xd3_hinst *inst) 299 | { 300 | printf("xd3_decode_parse_halfinst\n"); 301 | int ret; 302 | 303 | /* If the size from the instruction table is zero then read a size value. */ 304 | int needSize = inst->size == 0; 305 | if ((inst->size == 0) && 306 | (ret = xd3_read_size (stream, 307 | & stream->inst_sect.buf, 308 | stream->inst_sect.buf_max, 309 | & inst->size))) 310 | { 311 | return XD3_INVALID_INPUT; 312 | } 313 | if (needSize) { 314 | printf("read inst size = %d\n", inst->size); 315 | } 316 | 317 | /* For copy instructions, read address. */ 318 | if (inst->type >= XD3_CPY) 319 | { 320 | IF_DEBUG2 ({ 321 | static int cnt = 0; 322 | XPR(NT "DECODE:%u: COPY at %"Q"u (winoffset %"W"u) " 323 | "size %"W"u winaddr %"W"u\n", 324 | cnt++, 325 | stream->total_out + (stream->dec_position - 326 | stream->dec_cpylen), 327 | (stream->dec_position - stream->dec_cpylen), 328 | inst->size, 329 | inst->addr); 330 | }); 331 | 332 | printf("dec_position = %d\n", stream->dec_position); 333 | printf("mode = %d\n", inst->type - XD3_CPY); 334 | if ((ret = xd3_decode_address (stream, 335 | stream->dec_position, 336 | inst->type - XD3_CPY, 337 | & stream->addr_sect.buf, 338 | stream->addr_sect.buf_max, 339 | & inst->addr))) 340 | { 341 | return ret; 342 | } 343 | printf("XD3_CPY address = %d\n", inst->addr); 344 | 345 | /* Cannot copy an address before it is filled-in. */ 346 | if (inst->addr >= stream->dec_position) 347 | { 348 | stream->msg = "address too large"; 349 | return XD3_INVALID_INPUT; 350 | } 351 | 352 | /* Check: a VCD_TARGET or VCD_SOURCE copy cannot exceed the remaining 353 | * buffer space in its own segment. */ 354 | if (inst->addr < stream->dec_cpylen && 355 | inst->addr + inst->size > stream->dec_cpylen) 356 | { 357 | stream->msg = "size too large"; 358 | return XD3_INVALID_INPUT; 359 | } 360 | } 361 | else 362 | { 363 | IF_DEBUG2 ({ 364 | if (inst->type == XD3_ADD) 365 | { 366 | static int cnt; 367 | XPR(NT "DECODE:%d: ADD at %"Q"u (winoffset %"W"u) size %"W"u\n", 368 | cnt++, 369 | (stream->total_out + stream->dec_position - stream->dec_cpylen), 370 | stream->dec_position - stream->dec_cpylen, 371 | inst->size); 372 | } 373 | else 374 | { 375 | static int cnt; 376 | XD3_ASSERT (inst->type == XD3_RUN); 377 | XPR(NT "DECODE:%d: RUN at %"Q"u (winoffset %"W"u) size %"W"u\n", 378 | cnt++, 379 | stream->total_out + stream->dec_position - stream->dec_cpylen, 380 | stream->dec_position - stream->dec_cpylen, 381 | inst->size); 382 | } 383 | }); 384 | } 385 | 386 | /* Check: The instruction will not overflow the output buffer. */ 387 | if (stream->dec_position + inst->size > stream->dec_maxpos) 388 | { 389 | stream->msg = "size too large"; 390 | return XD3_INVALID_INPUT; 391 | } 392 | 393 | printf("dec_position = %d\n", stream->dec_position); 394 | printf("inst size = %d\n", inst->size); 395 | stream->dec_position += inst->size; 396 | printf("dec_position = %d\n", stream->dec_position); 397 | return 0; 398 | } 399 | 400 | /* Decode a single opcode and then decode the two half-instructions. */ 401 | static int 402 | xd3_decode_instruction (xd3_stream *stream) 403 | { 404 | static int instCount = 0; 405 | printf("xd3_decode_instruction %d\n", instCount++); 406 | int ret; 407 | const xd3_dinst *inst; 408 | 409 | if (stream->inst_sect.buf == stream->inst_sect.buf_max) 410 | { 411 | stream->msg = "instruction underflow"; 412 | return XD3_INVALID_INPUT; 413 | } 414 | 415 | printf("instPair = %d\n", *stream->inst_sect.buf); 416 | inst = &stream->code_table[*stream->inst_sect.buf++]; 417 | 418 | stream->dec_current1.type = inst->type1; 419 | stream->dec_current2.type = inst->type2; 420 | stream->dec_current1.size = inst->size1; 421 | stream->dec_current2.size = inst->size2; 422 | printf("%s/%d/%d : %s/%d/%d\n", 423 | typeToTypeString(inst->type1), inst->size1, typeToMode(inst->type1), 424 | typeToTypeString(inst->type2), inst->size2, typeToMode(inst->type2)); 425 | 426 | /* For each instruction with a real operation, decode the 427 | * corresponding size and addresses if necessary. Assume a 428 | * code-table may have NOOP in either position, although this is 429 | * unlikely. */ 430 | if (inst->type1 != XD3_NOOP && 431 | (ret = xd3_decode_parse_halfinst (stream, & stream->dec_current1))) 432 | { 433 | return ret; 434 | } 435 | if (inst->type2 != XD3_NOOP && 436 | (ret = xd3_decode_parse_halfinst (stream, & stream->dec_current2))) 437 | { 438 | return ret; 439 | } 440 | return 0; 441 | } 442 | 443 | static void 444 | dumpBytes(const uint8_t* p, int len) { 445 | printf("++++++++++++++++++++++++++++++++++++++++++\n"); 446 | uint8_t buf[9]; 447 | int i; 448 | for (i = 0; i < len; i++) { 449 | //printf("0x%02x, ", p[i]); 450 | printf("%3d, ", p[i]); 451 | if (p[i] >= 32 && p[i] < 127) { 452 | buf[i % 8] = p[i]; 453 | } else { 454 | buf[i % 8] = ' '; 455 | } 456 | if (i % 8 == 7) { 457 | buf[8] = '\0'; 458 | printf(" // '%s'\n", buf); 459 | } 460 | } 461 | i--; 462 | if (i % 8 != 7) { 463 | buf[(i % 8) + 1] = '\0'; 464 | printf(" // '%s'\n", buf); 465 | } 466 | printf("++++++++++++++++++++++++++++++++++++++++++\n"); 467 | } 468 | 469 | /* Output the result of a single half-instruction. OPT: This the 470 | decoder hotspot. Modifies "hinst", see below. */ 471 | static int 472 | xd3_decode_output_halfinst (xd3_stream *stream, xd3_hinst *inst) 473 | { 474 | printf("xd3_decode_output_halfinst: type=%d, addr=%d, size=%d\n", inst->type, inst->addr, inst->size); 475 | /* This method is reentrant for copy instructions which may return 476 | * XD3_GETSRCBLK to the caller. Each time through a copy takes the 477 | * minimum of inst->size and the available space on whichever block 478 | * supplies the data */ 479 | usize_t take = inst->size; 480 | //printf("take = %d (line %d)\n", take, __LINE__); 481 | int start_pos = stream->avail_out; 482 | printf("start_pos = %d\n", start_pos); 483 | 484 | if (USIZE_T_OVERFLOW (stream->avail_out, take) || 485 | stream->avail_out + take > stream->space_out) 486 | { 487 | stream->msg = "overflow while decoding"; 488 | return XD3_INVALID_INPUT; 489 | } 490 | 491 | XD3_ASSERT (inst->type != XD3_NOOP); 492 | 493 | switch (inst->type) 494 | { 495 | case XD3_RUN: 496 | { 497 | /* Only require a single data byte. */ 498 | if (stream->data_sect.buf == stream->data_sect.buf_max) 499 | { 500 | stream->msg = "data underflow"; 501 | return XD3_INVALID_INPUT; 502 | } 503 | 504 | printf(" >>>> XD3_RUN: memset 0x%02x for %d\n", stream->data_sect.buf[0], take); 505 | memset (stream->next_out + stream->avail_out, 506 | stream->data_sect.buf[0], 507 | take); 508 | dumpBytes(stream->next_out + stream->avail_out, take); 509 | 510 | stream->data_sect.buf += 1; 511 | stream->avail_out += take; 512 | inst->type = XD3_NOOP; 513 | break; 514 | } 515 | case XD3_ADD: 516 | { 517 | /* Require at least TAKE data bytes. */ 518 | if (stream->data_sect.buf + take > stream->data_sect.buf_max) 519 | { 520 | stream->msg = "data underflow"; 521 | return XD3_INVALID_INPUT; 522 | } 523 | 524 | printf(" >>>> XD3_ADD: memcpy %d from the data_sect\n", take); 525 | memcpy (stream->next_out + stream->avail_out, 526 | stream->data_sect.buf, 527 | take); 528 | dumpBytes(stream->next_out + stream->avail_out, take); 529 | 530 | stream->data_sect.buf += take; 531 | stream->avail_out += take; 532 | inst->type = XD3_NOOP; 533 | break; 534 | } 535 | default: 536 | { 537 | printf(" >>>> XD3_CPY\n"); 538 | usize_t i; 539 | const uint8_t *src; 540 | uint8_t *dst; 541 | int overlap; 542 | 543 | /* See if it copies from the VCD_TARGET/VCD_SOURCE window or 544 | * the target window. Out-of-bounds checks for the addresses 545 | * and sizes are performed in xd3_decode_parse_halfinst. This 546 | * if/else must set "overlap", "src", and "dst". */ 547 | //printf("inst->addr = %d (line %d)\n", inst->addr, __LINE__); 548 | printf("dec_cpylen = %d\n", stream->dec_cpylen); 549 | if (inst->addr < stream->dec_cpylen) 550 | { 551 | /* In both branches we are copying from outside the 552 | * current decoder window, the first (VCD_TARGET) is 553 | * unimplemented. */ 554 | overlap = 0; 555 | printf("overlap = 0\n"); 556 | 557 | /* This branch sets "src". As a side-effect, we modify 558 | * "inst" so that if we reenter this method after a 559 | * XD3_GETSRCBLK response the state is correct. So if the 560 | * instruction can be fulfilled by a contiguous block of 561 | * memory then we will set: 562 | * 563 | * inst->type = XD3_NOOP; 564 | * inst->size = 0; 565 | */ 566 | if (stream->dec_win_ind & VCD_TARGET) 567 | { 568 | /* TODO: Users have requested long-distance copies of 569 | * similar material within a target (e.g., for dup 570 | * supression in backups). This code path is probably 571 | * dead due to XD3_UNIMPLEMENTED in xd3_decode_setup_buffers */ 572 | inst->size = 0; 573 | inst->type = XD3_NOOP; 574 | stream->msg = "VCD_TARGET not implemented"; 575 | return XD3_UNIMPLEMENTED; 576 | } 577 | else 578 | { 579 | /* In this case we have to read a source block, which 580 | * could return control to the caller. We need to 581 | * know the first block number needed for this 582 | * copy. */ 583 | //printf("read source block(%s/%d)\n", __FILE__, __LINE__); 584 | printf("read source block\n"); 585 | xd3_source *source = stream->src; 586 | xoff_t block = source->cpyoff_blocks; 587 | usize_t blkoff = source->cpyoff_blkoff; 588 | const usize_t blksize = source->blksize; 589 | int ret; 590 | 591 | if (block !=0) { 592 | printf("block = %d\n", block); 593 | printf("source->blksize = %d\n", source->blksize); 594 | } 595 | printf("blkoff = %d\n", blkoff); 596 | printf("add inst addr to blkoff\n"); 597 | xd3_blksize_add (&block, &blkoff, source, inst->addr); 598 | XD3_ASSERT (blkoff < blksize); 599 | if (block !=0) { 600 | printf("block = %d\n", block); 601 | } 602 | printf("blkoff = %d\n", blkoff); 603 | 604 | if ((ret = xd3_getblk (stream, block))) 605 | { 606 | /* could be a XD3_GETSRCBLK failure. */ 607 | if (ret == XD3_TOOFARBACK) 608 | { 609 | stream->msg = "non-seekable source in decode"; 610 | ret = XD3_INTERNAL; 611 | } 612 | return ret; 613 | } 614 | 615 | src = source->curblk + blkoff; 616 | //printf("source->curblk=%d, blkoff=%d\n", source->curblk, blkoff); 617 | 618 | /* This block is either full, or a partial block that 619 | * must contain enough bytes. */ 620 | if ((source->onblk != blksize) && 621 | (blkoff + take > source->onblk)) 622 | { 623 | IF_DEBUG1 (XPR(NT "[srcfile] short at blkno %"Q"u onblk " 624 | "%"W"u blksize %"W"u blkoff %"W"u take %"W"u\n", 625 | block, 626 | source->onblk, 627 | blksize, 628 | blkoff, 629 | take)); 630 | stream->msg = "source file too short"; 631 | return XD3_INVALID_INPUT; 632 | } 633 | 634 | XD3_ASSERT (blkoff != blksize); 635 | 636 | /* Check if we have enough data on this block to 637 | * finish the instruction. */ 638 | if (blkoff + take <= blksize) 639 | { 640 | inst->type = XD3_NOOP; 641 | inst->size = 0; 642 | } 643 | else 644 | { 645 | take = blksize - blkoff; 646 | inst->size -= take; 647 | inst->addr += take; 648 | 649 | /* because (blkoff + take > blksize), above */ 650 | XD3_ASSERT (inst->size != 0); 651 | } 652 | } 653 | } 654 | else 655 | { 656 | /* TODO: the memcpy/overlap optimization, etc. Overlap 657 | * here could be more specific, it's whether (inst->addr - 658 | * srclen) + inst->size > input_pos ? And is the system 659 | * memcpy really any good? */ 660 | overlap = 1; 661 | printf("overlap = 1\n"); 662 | 663 | /* For a target-window copy, we know the entire range is 664 | * in-memory. The dec_tgtaddrbase is negatively offset by 665 | * dec_cpylen because the addresses start beyond that 666 | * point. */ 667 | //printf("stream->dec_tgtaddrbase = %d\n", stream->dec_tgtaddrbase); 668 | //printf("inst->addr = %d\n", inst->addr); 669 | //printf("stream->dec_tgtaddrbase_delta = %d\n", stream->dec_tgtaddrbase - stream->dec_buffer); 670 | int overlap_pos = inst->addr - stream->dec_cpylen; 671 | printf("overlap_pos = %d\n", overlap_pos); 672 | src = stream->dec_tgtaddrbase + inst->addr; 673 | //printf("src = %d (%s/%d)\n", src,__FILE__, __LINE__); 674 | inst->type = XD3_NOOP; 675 | inst->size = 0; 676 | } 677 | 678 | //printf("next_out = %d, avail_out = %d\n", stream->next_out, stream->avail_out); 679 | dst = stream->next_out + stream->avail_out; 680 | 681 | stream->avail_out += take; 682 | //printf("avail_out = %d\n", stream->avail_out); 683 | 684 | if (overlap) 685 | { 686 | printf(" <<<< manually copy %d\n", take); 687 | uint8_t* p = dst; 688 | /* Can't just memcpy here due to possible overlap. */ 689 | for (i = take; i != 0; i -= 1) 690 | { 691 | *dst++ = *src++; 692 | } 693 | dumpBytes(p, take); 694 | } 695 | else 696 | { 697 | printf(" <<<< memcopy take=%d\n", take); 698 | memcpy (dst, src, take); 699 | dumpBytes(dst, take); 700 | } 701 | } 702 | } 703 | 704 | return 0; 705 | } 706 | 707 | static int 708 | xd3_decode_finish_window (xd3_stream *stream) 709 | { 710 | printf("xd3_decode_finish_window\n"); 711 | stream->dec_winbytes = 0; 712 | stream->dec_state = DEC_FINISH; 713 | 714 | stream->data_sect.pos = 0; 715 | stream->inst_sect.pos = 0; 716 | stream->addr_sect.pos = 0; 717 | 718 | return XD3_OUTPUT; 719 | } 720 | 721 | static int 722 | xd3_decode_secondary_sections (xd3_stream *secondary_stream) 723 | { 724 | #if SECONDARY_ANY 725 | int ret; 726 | printf("xd3_decode_secondary_sections\n"); 727 | #define DECODE_SECONDARY_SECTION(UPPER,LOWER) \ 728 | ((secondary_stream->dec_del_ind & VCD_ ## UPPER ## COMP) && \ 729 | (ret = xd3_decode_secondary (secondary_stream, \ 730 | & secondary_stream-> LOWER ## _sect, \ 731 | & xd3_sec_ ## LOWER (secondary_stream)))) 732 | 733 | if (DECODE_SECONDARY_SECTION (DATA, data) || 734 | DECODE_SECONDARY_SECTION (INST, inst) || 735 | DECODE_SECONDARY_SECTION (ADDR, addr)) 736 | { 737 | return ret; 738 | } 739 | #undef DECODE_SECONDARY_SECTION 740 | #endif 741 | return 0; 742 | } 743 | 744 | static int 745 | xd3_decode_sections (xd3_stream *stream) 746 | { 747 | printf("xd3_decode_sections\n"); 748 | usize_t need, more, take; 749 | int copy, ret; 750 | 751 | if ((stream->flags & XD3_JUST_HDR) != 0) 752 | { 753 | /* Nothing left to do. */ 754 | return xd3_decode_finish_window (stream); 755 | } 756 | 757 | /* To avoid extra copying, allocate three sections at once (but 758 | * check for overflow). */ 759 | need = stream->inst_sect.size; 760 | 761 | if (USIZE_T_OVERFLOW (need, stream->addr_sect.size)) 762 | { 763 | stream->msg = "decoder section size overflow"; 764 | return XD3_INTERNAL; 765 | } 766 | need += stream->addr_sect.size; 767 | 768 | if (USIZE_T_OVERFLOW (need, stream->data_sect.size)) 769 | { 770 | stream->msg = "decoder section size overflow"; 771 | return XD3_INTERNAL; 772 | } 773 | need += stream->data_sect.size; 774 | 775 | /* The window may be entirely processed. */ 776 | XD3_ASSERT (stream->dec_winbytes <= need); 777 | 778 | /* Compute how much more input is needed. */ 779 | more = (need - stream->dec_winbytes); 780 | 781 | /* How much to consume. */ 782 | take = xd3_min (more, stream->avail_in); 783 | 784 | /* See if the input is completely available, to avoid copy. */ 785 | copy = (take != more); 786 | //printf("copy = %d\n", copy); 787 | 788 | /* If the window is skipped... */ 789 | if ((stream->flags & XD3_SKIP_WINDOW) != 0) 790 | { 791 | printf("skip window\n"); 792 | /* Skip the available input. */ 793 | DECODE_INPUT (take); 794 | 795 | printf("skip %d bytes\n", take); 796 | stream->dec_winbytes += take; 797 | 798 | if (copy) 799 | { 800 | stream->msg = "further input required"; 801 | return XD3_INPUT; 802 | } 803 | 804 | return xd3_decode_finish_window (stream); 805 | } 806 | 807 | /* Process all but the DATA section. */ 808 | switch (stream->dec_state) 809 | { 810 | default: 811 | stream->msg = "internal error"; 812 | return XD3_INVALID_INPUT; 813 | 814 | case DEC_DATA: 815 | if ((ret = xd3_decode_section (stream, & stream->data_sect, 816 | DEC_INST, copy))) { return ret; } 817 | case DEC_INST: 818 | if ((ret = xd3_decode_section (stream, & stream->inst_sect, 819 | DEC_ADDR, copy))) { return ret; } 820 | case DEC_ADDR: 821 | if ((ret = xd3_decode_section (stream, & stream->addr_sect, 822 | DEC_EMIT, copy))) { return ret; } 823 | } 824 | 825 | XD3_ASSERT (stream->dec_winbytes == need); 826 | 827 | if ((ret = xd3_decode_secondary_sections (stream))) { return ret; } 828 | 829 | if (stream->flags & XD3_SKIP_EMIT) 830 | { 831 | printf("skip emit\n"); 832 | return xd3_decode_finish_window (stream); 833 | } 834 | 835 | /* OPT: A possible optimization is to avoid allocating memory in 836 | * decode_setup_buffers and to avoid a large memcpy when the window 837 | * consists of a single VCD_SOURCE copy instruction. */ 838 | if ((ret = xd3_decode_setup_buffers (stream))) { return ret; } 839 | 840 | return 0; 841 | } 842 | 843 | static int 844 | xd3_decode_emit (xd3_stream *stream) 845 | { 846 | printf("\n\n"); 847 | printf("#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@\n"); 848 | printf(" xd3_decode_emit:\n"); 849 | printf("#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@\n"); 850 | int ret; 851 | 852 | /* Produce output: originally structured to allow reentrant code 853 | * that fills as much of the output buffer as possible, but VCDIFF 854 | * semantics allows to copy from anywhere from the target window, so 855 | * instead allocate a sufficiently sized buffer after the target 856 | * window length is decoded. 857 | * 858 | * This code still needs to be reentrant to allow XD3_GETSRCBLK to 859 | * return control. This is handled by setting the 860 | * stream->dec_currentN instruction types to XD3_NOOP after they 861 | * have been processed. */ 862 | XD3_ASSERT (! (stream->flags & XD3_SKIP_EMIT)); 863 | XD3_ASSERT (stream->dec_tgtlen <= stream->space_out); 864 | 865 | 866 | //printf("dec_current1.type = %d\n", stream->dec_current1.type); 867 | //printf("dec_current2.type = %d\n", stream->dec_current2.type); 868 | while (stream->inst_sect.buf != stream->inst_sect.buf_max || 869 | stream->dec_current1.type != XD3_NOOP || 870 | stream->dec_current2.type != XD3_NOOP) 871 | { 872 | /* Decode next instruction pair. */ 873 | printf("\n========== Decode next instruction pair ==========\n"); 874 | if ((stream->dec_current1.type == XD3_NOOP) && 875 | (stream->dec_current2.type == XD3_NOOP) && 876 | (ret = xd3_decode_instruction (stream))) { return ret; } 877 | 878 | /* Output dec_current1 */ 879 | while ((stream->dec_current1.type != XD3_NOOP)) 880 | { 881 | printf("output dec_current1 ----------\n"); 882 | if ((ret = xd3_decode_output_halfinst (stream, & stream->dec_current1))) 883 | { 884 | return ret; 885 | } 886 | } 887 | /* Output dec_current2 */ 888 | while (stream->dec_current2.type != XD3_NOOP) 889 | { 890 | printf("output dec_current2 ----------\n"); 891 | if ((ret = xd3_decode_output_halfinst (stream, & stream->dec_current2))) 892 | { 893 | return ret; 894 | } 895 | } 896 | } 897 | 898 | if (stream->avail_out != stream->dec_tgtlen) 899 | { 900 | IF_DEBUG2 (DP(RINT "AVAIL_OUT(%"W"u) != DEC_TGTLEN(%"W"u)\n", 901 | stream->avail_out, stream->dec_tgtlen)); 902 | stream->msg = "wrong window length"; 903 | return XD3_INVALID_INPUT; 904 | } 905 | 906 | if (stream->data_sect.buf != stream->data_sect.buf_max) 907 | { 908 | stream->msg = "extra data section"; 909 | return XD3_INVALID_INPUT; 910 | } 911 | 912 | if (stream->addr_sect.buf != stream->addr_sect.buf_max) 913 | { 914 | stream->msg = "extra address section"; 915 | return XD3_INVALID_INPUT; 916 | } 917 | 918 | /* OPT: Should cksum computation be combined with the above loop? */ 919 | printf("check checksum is VCD_ADLER32 set\n"); 920 | if ((stream->dec_win_ind & VCD_ADLER32) != 0 && 921 | (stream->flags & XD3_ADLER32_NOVER) == 0) 922 | { 923 | uint32_t a32 = adler32 (1L, stream->next_out, stream->avail_out); 924 | printf("a32 = %d\n", a32); 925 | 926 | if (a32 != stream->dec_adler32) 927 | { 928 | stream->msg = "target window checksum mismatch"; 929 | return XD3_INVALID_INPUT; 930 | } 931 | } 932 | 933 | /* Finished with a window. */ 934 | return xd3_decode_finish_window (stream); 935 | } 936 | 937 | int 938 | xd3_decode_input (xd3_stream *stream) 939 | { 940 | int ret; 941 | int srcortgt; 942 | 943 | if (stream->enc_state != 0) 944 | { 945 | stream->msg = "encoder/decoder transition"; 946 | return XD3_INVALID_INPUT; 947 | } 948 | 949 | #define BYTE_CASE(expr,x,nstate) \ 950 | do { \ 951 | if ( (expr) && \ 952 | ((ret = xd3_decode_byte (stream, & (x))) != 0) ) { return ret; } \ 953 | stream->dec_state = (nstate); \ 954 | } while (0) 955 | 956 | #define OFFSET_CASE(expr,x,nstate) \ 957 | do { \ 958 | if ( (expr) && \ 959 | ((ret = xd3_decode_offset (stream, & (x))) != 0) ) { return ret; } \ 960 | stream->dec_state = (nstate); \ 961 | } while (0) 962 | 963 | #define SIZE_CASE(expr,x,nstate) \ 964 | do { \ 965 | if ( (expr) && \ 966 | ((ret = xd3_decode_size (stream, & (x))) != 0) ) { return ret; } \ 967 | stream->dec_state = (nstate); \ 968 | } while (0) 969 | 970 | switch (stream->dec_state) 971 | { 972 | case DEC_VCHEAD: 973 | { 974 | printf("==================================\n"); 975 | printf(" HEADER pos = %d\n", stream->total_in); 976 | printf("==================================\n"); 977 | printf("DEC_VCHEAD: load magic bytes\n"); 978 | if ((ret = xd3_decode_bytes (stream, stream->dec_magic, 979 | & stream->dec_magicbytes, 4))) 980 | { 981 | return ret; 982 | } 983 | dumpBytes(stream->dec_magic, 4); 984 | 985 | printf("DEC_VCHEAD: check magic bytes\n"); 986 | if (stream->dec_magic[0] != VCDIFF_MAGIC1 || 987 | stream->dec_magic[1] != VCDIFF_MAGIC2 || 988 | stream->dec_magic[2] != VCDIFF_MAGIC3) 989 | { 990 | stream->msg = "not a VCDIFF input"; 991 | return XD3_INVALID_INPUT; 992 | } 993 | 994 | if (stream->dec_magic[3] != 0) 995 | { 996 | stream->msg = "VCDIFF input version > 0 is not supported"; 997 | return XD3_INVALID_INPUT; 998 | } 999 | 1000 | stream->dec_state = DEC_HDRIND; 1001 | } 1002 | case DEC_HDRIND: 1003 | { 1004 | if ((ret = xd3_decode_byte (stream, & stream->dec_hdr_ind))) 1005 | { 1006 | return ret; 1007 | } 1008 | printf("DEC_HDRIND: dec_hdr_ind = 0x%02x\n", stream->dec_hdr_ind); 1009 | 1010 | //printf("DEC_HDRIND: check only lower 3 bits of hdr indicator set\n"); 1011 | if ((stream->dec_hdr_ind & VCD_INVHDR) != 0) 1012 | { 1013 | stream->msg = "unrecognized header indicator bits set"; 1014 | return XD3_INVALID_INPUT; 1015 | } 1016 | 1017 | stream->dec_state = DEC_SECONDID; 1018 | } 1019 | 1020 | case DEC_SECONDID: 1021 | /* Secondary compressor ID: only if VCD_SECONDARY is set */ 1022 | printf("DEC_SECONDID: read byte if VCD_SECONDARY(%d)\n", stream->dec_hdr_ind & VCD_SECONDARY); 1023 | if ((stream->dec_hdr_ind & VCD_SECONDARY) != 0) 1024 | { 1025 | BYTE_CASE (1, stream->dec_secondid, DEC_TABLEN); 1026 | printf("DEC_SECONDID = %d\n", stream->dec_secondid); 1027 | 1028 | switch (stream->dec_secondid) 1029 | { 1030 | case VCD_FGK_ID: 1031 | FGK_CASE (stream); 1032 | case VCD_DJW_ID: 1033 | DJW_CASE (stream); 1034 | case VCD_LZMA_ID: 1035 | LZMA_CASE (stream); 1036 | default: 1037 | stream->msg = "unknown secondary compressor ID"; 1038 | return XD3_INVALID_INPUT; 1039 | } 1040 | } 1041 | 1042 | case DEC_TABLEN: 1043 | /* Length of code table data: only if VCD_CODETABLE is set */ 1044 | printf("DEC_TABLEN: read size if VCD_CODETABLE(%d)\n", stream->dec_hdr_ind & VCD_CODETABLE); 1045 | SIZE_CASE ((stream->dec_hdr_ind & VCD_CODETABLE) != 0, 1046 | stream->dec_codetblsz, DEC_NEAR); 1047 | 1048 | /* The codetblsz counts the two NEAR/SAME bytes */ 1049 | if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0) { 1050 | printf("DEC_TABLEN: \n"); 1051 | if (stream->dec_codetblsz <= 2) { 1052 | stream->msg = "invalid code table size"; 1053 | return ENOMEM; 1054 | } 1055 | stream->dec_codetblsz -= 2; 1056 | } 1057 | case DEC_NEAR: 1058 | /* Near modes: only if VCD_CODETABLE is set */ 1059 | printf("DEC_NEAR: read byte if VCD_CODETABLE(%d)\n", stream->dec_hdr_ind & VCD_CODETABLE); 1060 | BYTE_CASE((stream->dec_hdr_ind & VCD_CODETABLE) != 0, 1061 | stream->acache.s_near, DEC_SAME); 1062 | case DEC_SAME: 1063 | /* Same modes: only if VCD_CODETABLE is set */ 1064 | printf("DEC_SAME: read byte if VCD_CODETABLE(%d)\n", stream->dec_hdr_ind & VCD_CODETABLE); 1065 | BYTE_CASE((stream->dec_hdr_ind & VCD_CODETABLE) != 0, 1066 | stream->acache.s_same, DEC_TABDAT); 1067 | case DEC_TABDAT: 1068 | /* Compressed code table data */ 1069 | 1070 | if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0) 1071 | { 1072 | stream->msg = "VCD_CODETABLE support was removed"; 1073 | return XD3_UNIMPLEMENTED; 1074 | } 1075 | else 1076 | { 1077 | printf("use the default table.\n"); 1078 | /* Use the default table. */ 1079 | printf("use default table near_modes %d\n", __rfc3284_code_table_desc.near_modes); 1080 | stream->acache.s_near = __rfc3284_code_table_desc.near_modes; 1081 | printf("use default table same_modes %d\n", __rfc3284_code_table_desc.same_modes); 1082 | stream->acache.s_same = __rfc3284_code_table_desc.same_modes; 1083 | stream->code_table = xd3_rfc3284_code_table (); 1084 | } 1085 | 1086 | if ((ret = xd3_alloc_cache (stream))) { return ret; } 1087 | 1088 | stream->dec_state = DEC_APPLEN; 1089 | 1090 | case DEC_APPLEN: 1091 | /* Length of application data */ 1092 | printf("DEC_APPLEN: read size if VCD_APPHEADER(%d)\n", stream->dec_hdr_ind & VCD_APPHEADER); 1093 | SIZE_CASE((stream->dec_hdr_ind & VCD_APPHEADER) != 0, 1094 | stream->dec_appheadsz, DEC_APPDAT); 1095 | if (stream->dec_hdr_ind & VCD_APPHEADER) { 1096 | printf("dec_appheadsz = %d\n", stream->dec_appheadsz); 1097 | } 1098 | 1099 | case DEC_APPDAT: 1100 | /* Application data */ 1101 | if (stream->dec_hdr_ind & VCD_APPHEADER) 1102 | { 1103 | printf("DEC_APPDAT: \n"); 1104 | /* Note: we add an additional byte for padding, to allow 1105 | 0-termination. Check for overflow: */ 1106 | if (USIZE_T_OVERFLOW(stream->dec_appheadsz, 1)) 1107 | { 1108 | stream->msg = "exceptional appheader size"; 1109 | return XD3_INVALID_INPUT; 1110 | } 1111 | 1112 | if ((stream->dec_appheader == NULL) && 1113 | (stream->dec_appheader = 1114 | (uint8_t*) xd3_alloc (stream, 1115 | stream->dec_appheadsz+1, 1)) == NULL) 1116 | { 1117 | return ENOMEM; 1118 | } 1119 | 1120 | stream->dec_appheader[stream->dec_appheadsz] = 0; 1121 | 1122 | if ((ret = xd3_decode_bytes (stream, stream->dec_appheader, 1123 | & stream->dec_appheadbytes, 1124 | stream->dec_appheadsz))) 1125 | { 1126 | return ret; 1127 | } 1128 | dumpBytes(stream->dec_appheader, stream->dec_appheadsz + 1); 1129 | } 1130 | 1131 | /* xoff_t -> usize_t is safe because this is the first block. */ 1132 | stream->dec_hdrsize = (usize_t) stream->total_in; 1133 | printf("pos after dec_appheader = %d\n", stream->total_in); 1134 | printf("\n"); 1135 | stream->dec_state = DEC_WININD; 1136 | 1137 | case DEC_WININD: 1138 | { 1139 | printf("DEC_WININD\n"); 1140 | printf("==================================\n"); 1141 | printf(" WINDOW pos = %d\n", stream->total_in); 1142 | printf("==================================\n"); 1143 | /* Start of a window: the window indicator */ 1144 | if ((ret = xd3_decode_byte (stream, & stream->dec_win_ind))) 1145 | { 1146 | return ret; 1147 | } 1148 | printf("dec_win_ind = %d(0x%02x)\n", stream->dec_win_ind, stream->dec_win_ind); 1149 | printf("dec_tgtlen = %d(0x%02x)\n", stream->dec_tgtlen, stream->dec_tgtlen); 1150 | 1151 | printf("window_count = %d\n", stream->dec_window_count); 1152 | stream->current_window = stream->dec_window_count; 1153 | 1154 | if (XOFF_T_OVERFLOW (stream->dec_winstart, stream->dec_tgtlen)) 1155 | { 1156 | stream->msg = "decoder file offset overflow"; 1157 | return XD3_INVALID_INPUT; 1158 | } 1159 | 1160 | stream->dec_winstart += stream->dec_tgtlen; 1161 | printf("dec_winstart = %d\n", stream->dec_winstart); 1162 | 1163 | if ((stream->dec_win_ind & VCD_INVWIN) != 0) 1164 | { 1165 | stream->msg = "unrecognized window indicator bits set"; 1166 | return XD3_INVALID_INPUT; 1167 | } 1168 | 1169 | printf("xd3_decode_init_window\n"); 1170 | if ((ret = xd3_decode_init_window (stream))) { return ret; } 1171 | 1172 | stream->dec_state = DEC_CPYLEN; 1173 | 1174 | IF_DEBUG2 (DP(RINT "--------- TARGET WINDOW %"Q"u -----------\n", 1175 | stream->current_window)); 1176 | } 1177 | 1178 | case DEC_CPYLEN: 1179 | srcortgt = SRCORTGT(stream->dec_win_ind); 1180 | printf("srcortgt = %d\n", srcortgt); 1181 | /* Copy window length: only if VCD_SOURCE or VCD_TARGET is set */ 1182 | printf("DEC_CPYLEN: dec_cpylen = %d\n", stream->dec_cpylen); 1183 | printf("DEC_CPYLEN: get size if SRCORTGT(%d), pos = %d\n", srcortgt, stream->total_in); 1184 | SIZE_CASE(SRCORTGT (stream->dec_win_ind), stream->dec_cpylen, 1185 | DEC_CPYOFF); 1186 | if (SRCORTGT (stream->dec_win_ind)) { 1187 | printf("dec_cpylen = %d\n", stream->dec_cpylen); 1188 | } 1189 | 1190 | /* Set the initial, logical decoder position (HERE address) in 1191 | * dec_position. This is set to just after the source/copy 1192 | * window, as we are just about to output the first byte of 1193 | * target window. */ 1194 | stream->dec_position = stream->dec_cpylen; 1195 | 1196 | case DEC_CPYOFF: 1197 | /* Copy window offset: only if VCD_SOURCE or VCD_TARGET is set */ 1198 | printf("DEC_CPYOFF: get size if SRCORTGT(%d), pos = %d\n", srcortgt, stream->total_in); 1199 | OFFSET_CASE(SRCORTGT (stream->dec_win_ind), stream->dec_cpyoff, 1200 | DEC_ENCLEN); 1201 | if (SRCORTGT (stream->dec_win_ind)) { 1202 | printf("dec_cpyoff = %d\n", stream->dec_cpyoff); 1203 | } 1204 | 1205 | /* Copy offset and copy length may not overflow. */ 1206 | if (XOFF_T_OVERFLOW (stream->dec_cpyoff, stream->dec_cpylen)) 1207 | { 1208 | stream->msg = "decoder copy window overflows a file offset"; 1209 | return XD3_INVALID_INPUT; 1210 | } 1211 | 1212 | /* Check copy window bounds: VCD_TARGET window may not exceed 1213 | current position. */ 1214 | if ((stream->dec_win_ind & VCD_TARGET) && 1215 | (stream->dec_cpyoff + stream->dec_cpylen > 1216 | stream->dec_winstart)) 1217 | { 1218 | stream->msg = "VCD_TARGET window out of bounds"; 1219 | return XD3_INVALID_INPUT; 1220 | } 1221 | 1222 | case DEC_ENCLEN: 1223 | /* Length of the delta encoding */ 1224 | SIZE_CASE(1, stream->dec_enclen, DEC_TGTLEN); 1225 | printf("DEC_ENCLEN: dec_enclen = %d\n", stream->dec_enclen); 1226 | case DEC_TGTLEN: 1227 | /* Length of target window */ 1228 | SIZE_CASE(1, stream->dec_tgtlen, DEC_DELIND); 1229 | printf("DEC_TGTLEN: dec_tgtlen = %d\n", stream->dec_tgtlen); 1230 | //printf("\n"); 1231 | //printf("target window length = %d\n", stream->dec_tgtlen); 1232 | //printf("\n"); 1233 | 1234 | /* Set the maximum decoder position, beyond which we should not 1235 | * decode any data. This is the maximum value for dec_position. 1236 | * This may not exceed the size of a usize_t. */ 1237 | if (USIZE_T_OVERFLOW (stream->dec_cpylen, stream->dec_tgtlen)) 1238 | { 1239 | stream->msg = "decoder target window overflows a usize_t"; 1240 | return XD3_INVALID_INPUT; 1241 | } 1242 | 1243 | /* Check for malicious files. */ 1244 | if (stream->dec_tgtlen > XD3_HARDMAXWINSIZE) 1245 | { 1246 | stream->msg = "hard window size exceeded"; 1247 | return XD3_INVALID_INPUT; 1248 | } 1249 | 1250 | stream->dec_maxpos = stream->dec_cpylen + stream->dec_tgtlen; 1251 | 1252 | case DEC_DELIND: 1253 | /* Delta indicator */ 1254 | BYTE_CASE(1, stream->dec_del_ind, DEC_DATALEN); 1255 | printf("DEC_DELIND: dec_del_ind = %d\n", stream->dec_del_ind); 1256 | 1257 | if ((stream->dec_del_ind & VCD_INVDEL) != 0) 1258 | { 1259 | stream->msg = "unrecognized delta indicator bits set"; 1260 | return XD3_INVALID_INPUT; 1261 | } 1262 | 1263 | /* Delta indicator is only used with secondary compression. */ 1264 | if ((stream->dec_del_ind != 0) && (stream->sec_type == NULL)) 1265 | { 1266 | stream->msg = "invalid delta indicator bits set"; 1267 | return XD3_INVALID_INPUT; 1268 | } 1269 | 1270 | /* Section lengths */ 1271 | case DEC_DATALEN: 1272 | SIZE_CASE(1, stream->data_sect.size, DEC_INSTLEN); 1273 | printf("DEC_DATALEN: data_sect size = %d\n", stream->data_sect.size); 1274 | case DEC_INSTLEN: 1275 | SIZE_CASE(1, stream->inst_sect.size, DEC_ADDRLEN); 1276 | printf("DEC_INSTLEN: inst_sect size = %d\n", stream->inst_sect.size); 1277 | case DEC_ADDRLEN: 1278 | SIZE_CASE(1, stream->addr_sect.size, DEC_CKSUM); 1279 | printf("DEC_ADDRLEN: addr_sect size = %d\n", stream->addr_sect.size); 1280 | 1281 | case DEC_CKSUM: 1282 | printf("DEC_CKSUM: get checksum if VCD_ADLER32 pos = %d\n", stream->total_in); 1283 | /* Window checksum. */ 1284 | if ((stream->dec_win_ind & VCD_ADLER32) != 0) 1285 | { 1286 | int i; 1287 | 1288 | if ((ret = xd3_decode_bytes (stream, stream->dec_cksum, 1289 | & stream->dec_cksumbytes, 4))) 1290 | { 1291 | return ret; 1292 | } 1293 | dumpBytes(stream->dec_cksum, 4); 1294 | 1295 | for (i = 0; i < 4; i += 1) 1296 | { 1297 | stream->dec_adler32 = 1298 | (stream->dec_adler32 << 8) | stream->dec_cksum[i]; 1299 | } 1300 | printf("stream->dec_adler32 = %d\n", stream->dec_adler32); 1301 | } 1302 | 1303 | //printf("\n"); 1304 | stream->dec_state = DEC_DATA; 1305 | 1306 | /* Check dec_enclen for redundency, otherwise it is not really used. */ 1307 | { 1308 | usize_t enclen_check = 1309 | (1 + (xd3_sizeof_size (stream->dec_tgtlen) + 1310 | xd3_sizeof_size (stream->data_sect.size) + 1311 | xd3_sizeof_size (stream->inst_sect.size) + 1312 | xd3_sizeof_size (stream->addr_sect.size)) + 1313 | stream->data_sect.size + 1314 | stream->inst_sect.size + 1315 | stream->addr_sect.size + 1316 | ((stream->dec_win_ind & VCD_ADLER32) ? 4 : 0)); 1317 | 1318 | if (stream->dec_enclen != enclen_check) 1319 | { 1320 | stream->msg = "incorrect encoding length (redundent)"; 1321 | return XD3_INVALID_INPUT; 1322 | } 1323 | } 1324 | 1325 | /* Returning here gives the application a chance to inspect the 1326 | * header, skip the window, etc. */ 1327 | if (stream->current_window == 0) { return XD3_GOTHEADER; } 1328 | else { return XD3_WINSTART; } 1329 | 1330 | case DEC_DATA: 1331 | case DEC_INST: 1332 | case DEC_ADDR: 1333 | /* Next read the three sections. */ 1334 | if ((ret = xd3_decode_sections (stream))) { return ret; } 1335 | //printf("data_sect.size = %d\n", stream->data_sect.size); 1336 | dumpBytes(stream->data_sect.buf, stream->data_sect.size); 1337 | //printf("inst_sect.size = %d\n", stream->inst_sect.size); 1338 | dumpBytes(stream->inst_sect.buf, stream->inst_sect.size); 1339 | //printf("addr_sect.size = %d\n", stream->addr_sect.size); 1340 | dumpBytes(stream->addr_sect.buf, stream->addr_sect.size); 1341 | 1342 | case DEC_EMIT: 1343 | printf("DEC_EMIT:\n"); 1344 | 1345 | /* To speed VCD_SOURCE block-address calculations, the source 1346 | * cpyoff_blocks and cpyoff_blkoff are pre-computed. */ 1347 | if (stream->dec_win_ind & VCD_SOURCE) 1348 | { 1349 | xd3_source *src = stream->src; 1350 | 1351 | if (src == NULL) 1352 | { 1353 | stream->msg = "source input required"; 1354 | return XD3_INVALID_INPUT; 1355 | } 1356 | 1357 | //printf("++++++++++++++++\n"); 1358 | printf("stream->dec_cpyoff = %d\n", stream->dec_cpyoff); 1359 | //printf("src->cpyoff_blkoff = %d\n", src->cpyoff_blkoff); 1360 | xd3_blksize_div(stream->dec_cpyoff, src, 1361 | &src->cpyoff_blocks, 1362 | &src->cpyoff_blkoff); 1363 | //printf("stream->dec_cpyoff = %d\n", stream->dec_cpyoff); 1364 | //printf("src->cpyoff_blocks = %d\n", src->cpyoff_blocks); 1365 | printf("src->cpyoff_blkoff = %d\n", src->cpyoff_blkoff); 1366 | //printf("++++++++++++++++\n"); 1367 | 1368 | IF_DEBUG2(DP(RINT 1369 | "[decode_cpyoff] %"Q"u " 1370 | "cpyblkno %"Q"u " 1371 | "cpyblkoff %"W"u " 1372 | "blksize %"W"u\n", 1373 | stream->dec_cpyoff, 1374 | src->cpyoff_blocks, 1375 | src->cpyoff_blkoff, 1376 | src->blksize)); 1377 | } 1378 | 1379 | /* xd3_decode_emit returns XD3_OUTPUT on every success. */ 1380 | if ((ret = xd3_decode_emit (stream)) == XD3_OUTPUT) 1381 | { 1382 | stream->total_out += stream->avail_out; 1383 | } 1384 | 1385 | return ret; 1386 | 1387 | case DEC_FINISH: 1388 | { 1389 | if (stream->dec_win_ind & VCD_TARGET) 1390 | { 1391 | if (stream->dec_lastwin == NULL) 1392 | { 1393 | stream->dec_lastwin = stream->next_out; 1394 | stream->dec_lastspace = stream->space_out; 1395 | } 1396 | else 1397 | { 1398 | xd3_swap_uint8p (& stream->dec_lastwin, 1399 | & stream->next_out); 1400 | xd3_swap_usize_t (& stream->dec_lastspace, 1401 | & stream->space_out); 1402 | } 1403 | } 1404 | 1405 | stream->dec_lastlen = stream->dec_tgtlen; 1406 | stream->dec_laststart = stream->dec_winstart; 1407 | stream->dec_window_count += 1; 1408 | 1409 | /* Note: the updates to dec_winstart & current_window are 1410 | * deferred until after the next DEC_WININD byte is read. */ 1411 | stream->dec_state = DEC_WININD; 1412 | return XD3_WINFINISH; 1413 | } 1414 | 1415 | default: 1416 | stream->msg = "invalid state"; 1417 | return XD3_INVALID_INPUT; 1418 | } 1419 | } 1420 | 1421 | #endif // _XDELTA3_DECODE_H_ 1422 | -------------------------------------------------------------------------------- /xdelta3_decoder.js: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////// 2 | // // 3 | // DO NOT EDIT THIS FILE! // 4 | // // 5 | // THIS FILE WILL BE OVERWRITTEN DURING THE BUILD PROCESS! // 6 | // // 7 | // EDIT xdelta3_decoder_with_debug.js // 8 | // // 9 | // THIS FILE IS GENERATED FILE BY REMOVING THE DEBUG // 10 | // COMMENTS FROM xdelta3_decoder_with_debug.js // 11 | // // 12 | ///////////////////////////////////////////////////////////// 13 | 'use strict'; 14 | /** 15 | * xdelta3 - delta compression tools and library 16 | * Copyright 2016 Joshua MacDonald 17 | * 18 | * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 19 | * 2011, 2012, 2013, 2014, 2015 josh.macdonald@gmail.com 20 | * 21 | * Licensed under the Apache License, Version 2.0 (the "License"); 22 | * you may not use this file except in compliance with the License. 23 | * You may obtain a copy of the License at 24 | * 25 | * http://www.apache.org/licenses/LICENSE-2.0 26 | * 27 | * Unless required by applicable law or agreed to in writing, software 28 | * distributed under the License is distributed on an "AS IS" BASIS, 29 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 | * See the License for the specific language governing permissions and 31 | * limitations under the License. 32 | */ 33 | 34 | /** 35 | * @fileoverview This file implements XDelta3 decoding. 36 | * Note: the subroutine, method, field, and variable names do not follow 37 | * Javascript style guide but reflect the names in the XDelta3 C++ files. This 38 | * makes is to make it easier to keep this code in synch with the C++ code. 39 | * 40 | * The C++ code is very casual about initializing and accessing data structures. 41 | * This code is a port and follows that code style. 42 | */ 43 | 44 | (function() { 45 | 46 | // Check for namespace collision. 47 | if ((typeof window['XDelta3Decoder'] != 'undefined') 48 | || (typeof window.XDelta3Decoder != 'undefined')) { 49 | throw new Error('XDelta3Decoder already defined.'); 50 | } 51 | 52 | /** 53 | * The public class. 54 | */ 55 | window.XDelta3Decoder = function(debugOutput) { // 56 | }; 57 | 58 | var XDelta3Decoder = window.XDelta3Decoder; 59 | 60 | /** 61 | * The public API to decode a delta possibly with a source. 62 | * @param {!Uint8Array} delta The Xdelta delta file. 63 | * @param {Uint8Array=} opt_source The source file (optional). 64 | * @return {!ArrayBuffer} 65 | */ 66 | XDelta3Decoder.decode = function(delta, opt_source) { 67 | if (typeof opt_source != 'object') { 68 | opt_source = null; 69 | } 70 | var xdelta3 = new _XDelta3Decoder(delta, opt_source); 71 | var uint8Bytes = xdelta3.xd3_decode_input(); 72 | return uint8Bytes.buffer; 73 | } 74 | 75 | /** 76 | * The public API to disable debug printf code. 77 | */ 78 | 79 | // Define debug fallback routines if needed. 80 | 81 | /** 82 | * The XDelta3 data commands. 83 | */ 84 | /** @type {number} */ 85 | var XD3_NOOP = 0; 86 | /** @type {number} */ 87 | var XD3_ADD = 1; 88 | /** @type {number} */ 89 | var XD3_RUN = 2; 90 | /** @type {number} */ 91 | var XD3_CPY = 3; 92 | 93 | /** @type {number} */ 94 | var MIN_MATCH = 4; 95 | 96 | /** 97 | * Header indicator bits. 98 | */ 99 | /** @type {number} */ 100 | var VCD_SECONDARY = 1; 101 | /** @type {number} */ 102 | var VCD_CODETABLE = 2; 103 | /** @type {number} */ 104 | var VCD_APPHEADER = 4; 105 | /** @type {number} */ 106 | var VCD_INVHDR = ~(VCD_SECONDARY | VCD_CODETABLE | VCD_APPHEADER); 107 | 108 | var VCD_SOURCE = 0x01; 109 | var VCD_TARGET = 0x02; 110 | var VCD_ADLER32 = 0x04; 111 | 112 | 113 | /** 114 | * Declares the main decode class. 115 | * @param {!Uint8Array} delta The Xdelta3 delta file. 116 | * @param {Uint8Array=} opt_source The source file (optional). 117 | * @constructor 118 | */ 119 | function _XDelta3Decoder(delta, opt_source) { 120 | /** @type {!Uint8Array} */ 121 | this.delta = delta; 122 | 123 | var source = opt_source || new Uint8Array(1); 124 | /** @type {!DataObject} */ 125 | this.source = new DataObject(source); 126 | 127 | /** @type {!xd3_source} */ 128 | this.src = new xd3_source(); 129 | 130 | /** @type {number} */ 131 | this.position = 0; 132 | 133 | /** @type {number} */ 134 | this.dec_window_count = 0; 135 | 136 | /** @type {number} */ 137 | this.dec_winstart = 0; 138 | 139 | /** 140 | * The length of the target window. 141 | * @type {number} 142 | */ 143 | this.dec_tgtlen = 0; 144 | 145 | /** 146 | * The Alder32 checksum. This is used to verify the decoded bytes checksum 147 | * matches the checksum of the original. 148 | */ 149 | this.dec_adler32 = 0; 150 | 151 | /** 152 | * First half instruction. 153 | * @type {!xd3_hinst} 154 | */ 155 | this.dec_current1 = new xd3_hinst(); 156 | 157 | /** 158 | * Second half instruction. 159 | * @type {!xd3_hinst} 160 | */ 161 | this.dec_current2 = new xd3_hinst(); 162 | 163 | /** @type {!xd3_desect} */ 164 | this.data_sect = new xd3_desect(); 165 | 166 | /** @type {!xd3_desect} */ 167 | this.inst_sect = new xd3_desect(); 168 | 169 | /** @type {!xd3_desect} */ 170 | this.addr_sect = new xd3_desect(); 171 | 172 | /** 173 | * The address cache. 174 | * @type {!xd3_addr_cache} 175 | */ 176 | this.acache = new xd3_addr_cache( 177 | __rfc3284_code_table_desc.near_modes, 178 | __rfc3284_code_table_desc.same_modes); 179 | } 180 | 181 | /** 182 | * Allocates the address caches. 183 | */ 184 | _XDelta3Decoder.prototype.xd3_alloc_cache = function() { 185 | this.acache.near_array = null; // not sure this is needed 186 | this.acache.same_array = null; // not sure this is needed 187 | if (this.acache.s_near > 0) { 188 | this.acache.near_array = allocArray(this.acache.s_near, 0); 189 | } 190 | if (this.acache.s_same > 0) { 191 | this.acache.same_array = allocArray(this.acache.s_same * 256, 0); 192 | } 193 | }; 194 | 195 | /** 196 | * Parses the delta file data and produces the targetWindow data. 197 | * @return {!Uint8Array} 198 | */ 199 | _XDelta3Decoder.prototype.xd3_decode_input = function() { 200 | 201 | if (this.delta[0] != 0xD6 || // 'V' with MSB set 202 | this.delta[1] != 0xC3 || // 'C' with MSB set 203 | this.delta[2] != 0xC4 || // 'D' with MSB set 204 | this.delta[3] != 0) { // unused but be set to zero 205 | throw new Error('XD3_INVALID_INPUT invalid magic'); 206 | } 207 | this.position = 4; 208 | 209 | this.dec_hdr_ind = this.delta[this.position++]; 210 | if (this.dec_hdr_ind & VCD_INVHDR) { 211 | throw new Error('VCD_INVHDR unrecognized header indicator bits set'); 212 | } 213 | 214 | if (this.dec_hdr_ind & VCD_SECONDARY) { 215 | throw new Error('VCD_SECONDARY not implemented'); 216 | } 217 | 218 | if (this.dec_hdr_ind & VCD_CODETABLE) { 219 | throw new Error('VCD_CODETABLE support was removed'); 220 | } else { 221 | /* Use the default table. */ 222 | this.acache.s_near = __rfc3284_code_table_desc.near_modes; 223 | this.acache.s_same = __rfc3284_code_table_desc.same_modes; 224 | this.code_table = xd3_rfc3284_code_table(); 225 | } 226 | 227 | this.xd3_alloc_cache(); 228 | 229 | if (this.dec_hdr_ind & VCD_APPHEADER) { 230 | this.dec_appheadsz = this.getInteger(); 231 | // Note: appHeader does not have a 0-termination. 232 | this.dec_apphead = this.xd3_alloc(this.dec_appheadsz + 1); 233 | this.xd3_decode_bytes(this.dec_apphead, 0, this.dec_appheadsz); 234 | this.dec_apphead[this.dec_appheadsz + 1] = 0; 235 | } 236 | 237 | //var targetLength = 0; 238 | while (true) { 239 | if (this.position >= this.delta.length) { 240 | break; 241 | } 242 | //targetLength += 243 | this.handleWindow(); 244 | } 245 | return this.dec_buffer.bytes; 246 | }; 247 | 248 | _XDelta3Decoder.prototype.xd3_decode_init_window = function() { 249 | this.dec_cpylen = 0; 250 | this.dec_cpyoff = 0; 251 | // this.dec_cksumbytes = 0; 252 | 253 | xd3_init_cache(this.acache); 254 | } 255 | 256 | _XDelta3Decoder.prototype.handleWindow = function() { 257 | this.dec_win_ind = this.delta[this.position++]; // DEC_WININD 258 | 259 | if (this.dec_win_ind & ~7) { 260 | throw new Error('VCD_INVWIN unexpected bits set'); 261 | } 262 | 263 | this.current_window = this.dec_window_count; 264 | 265 | this.dec_winstart += this.dec_tgtlen; 266 | 267 | this.xd3_decode_init_window(); 268 | var SRCORTGT = VCD_SOURCE | VCD_TARGET; 269 | var srcortgt = SRCORTGT & this.dec_win_ind; 270 | 271 | // If using a source or target data segment: read the lenght and position 272 | // integers. 273 | if (srcortgt) { 274 | this.dec_cpylen = this.getInteger(); // DEC_CPYLEN 275 | } 276 | this.dec_position = this.dec_cpylen; 277 | if (srcortgt) { 278 | var sourcePosition = this.getInteger(); // DEC_CPYOFF 279 | this.dec_cpyoff = sourcePosition; 280 | } 281 | 282 | this.dec_enclen = this.getInteger(); // DEC_ENCLEN 283 | 284 | // Calculate the position if the delta was actually read. 285 | // var positionAfterDelta = this.position + this.dec_enclen; 286 | 287 | // Get the target window length. 288 | this.dec_tgtlen = this.getInteger(); // DEC_TGTLEN 289 | 290 | this.dec_del_ind = this.getByte(); // DEC_DELIND 291 | 292 | this.data_sect.size = this.getInteger(); // DEC_DATALEN 293 | this.inst_sect.size = this.getInteger(); // DEC_INSTLEN 294 | this.addr_sect.size = this.getInteger(); // DEC_ADDRLEN 295 | 296 | if (this.dec_win_ind & VCD_ADLER32) { // DEC_CKSUM 297 | this.dec_cksum = this.xd3_decode_allocate(4); 298 | for (var i = 0; i < 4; i += 1) { 299 | this.dec_adler32 = (this.dec_adler32 << 8) | this.dec_cksum[i]; 300 | } 301 | } 302 | 303 | this.xd3_decode_sections(); 304 | 305 | /* In the C++ code: 306 | * To speed VCD_SOURCE block-address calculations, the source 307 | * cpyoff_blocks and cpyoff_blkoff are pre-computed. 308 | * However, in this Javascript code there is no 'blocks'. 309 | */ 310 | if (this.dec_win_ind & VCD_SOURCE) { 311 | this.src.cpyoff_blkoff = this.dec_cpyoff; 312 | } 313 | this.xd3_decode_emit(); 314 | 315 | return this.dec_tgtlen; 316 | }; 317 | 318 | /** 319 | * This function only has code if the preprocessor statement 320 | * "#if SECONDARY_ANY" is set. SECONDARY_ANY does not seem to be set. 321 | */ 322 | _XDelta3Decoder.prototype.xd3_decode_secondary_sections = function() { // 323 | }; 324 | 325 | /** 326 | * @param {!xd3_desect} sect 327 | */ 328 | _XDelta3Decoder.prototype.xd3_decode_section = function(sect) { 329 | // It is possible to just point into the buffer but perhaps that can be done 330 | // later. 331 | sect.bytes = this.xd3_decode_allocate(sect.size); 332 | }; 333 | 334 | _XDelta3Decoder.prototype.xd3_decode_sections = function() { 335 | this.xd3_decode_section(this.data_sect); 336 | this.xd3_decode_section(this.inst_sect); 337 | this.xd3_decode_section(this.addr_sect); 338 | 339 | this.xd3_decode_secondary_sections(); 340 | 341 | this.xd3_decode_setup_buffers(); 342 | }; 343 | 344 | _XDelta3Decoder.prototype.xd3_decode_setup_buffers = function() { 345 | this.dec_buffer = new DataObject(new Uint8Array(this.dec_tgtlen)); 346 | }; 347 | 348 | var VCD_SELF = 0; 349 | var VCD_HERE = 1; 350 | 351 | /** 352 | * xd3_decode_address 353 | * @param {number} here 354 | * @param {number} mode 355 | * @param {!xd3_desect} sect 356 | */ 357 | _XDelta3Decoder.prototype.xd3_decode_address = function(here, mode, sect) { 358 | var val; 359 | var same_start = 2 + this.acache.s_near; 360 | 361 | if (mode < same_start) { 362 | val = sect.getInteger(); 363 | switch (mode) { 364 | case VCD_SELF: 365 | break; 366 | case VCD_HERE: 367 | // var old_val = val; 368 | val = here - val; 369 | break; 370 | default: 371 | val += this.acache.near_array[mode - 2]; 372 | } 373 | } else { 374 | mode -= same_start; 375 | var offset = sect.getByte(); 376 | val = this.acache.same_array[mode * 256 + offset]; 377 | } 378 | 379 | this.xd3_update_cache(this.acache, val); 380 | 381 | return val; 382 | }; 383 | 384 | /** 385 | * @param {!xd3_addr_cache} acache 386 | * @param {number} addr 387 | */ 388 | _XDelta3Decoder.prototype.xd3_update_cache = function(acache, addr) { 389 | if (acache.s_near > 0) { 390 | acache.near_array[acache.next_slot] = addr; 391 | acache.next_slot = (acache.next_slot + 1) % acache.s_near; 392 | } 393 | 394 | if (acache.s_same > 0) { 395 | acache.same_array[addr % (acache.s_same * 256)] = addr; 396 | } 397 | }; 398 | 399 | /** 400 | * @param {!xd3_hinst} inst 401 | */ 402 | _XDelta3Decoder.prototype.xd3_decode_output_halfinst = function(inst) { 403 | var take = inst.size; 404 | var blkoff; 405 | 406 | switch (inst.type) { 407 | case XD3_RUN: 408 | var val = this.data_sect.getByte(); 409 | this.dec_buffer.fill(val, take); 410 | break; 411 | 412 | case XD3_ADD: 413 | this.dec_buffer.copySect(this.data_sect, take); 414 | break; 415 | 416 | default: 417 | var overlap; 418 | var overlap_pos; 419 | if (inst.addr < this.dec_cpylen) { 420 | overlap = 0; 421 | if (this.dec_win_ind & VCD_TARGET) { 422 | throw new Error('VCD_TARGET not supported'); 423 | } else { 424 | blkoff = this.src.cpyoff_blkoff; 425 | blkoff = this.dec_cpyoff + inst.addr; 426 | } 427 | } else { 428 | overlap = 1; 429 | overlap_pos = inst.addr - this.dec_cpylen; 430 | } 431 | if (overlap) { 432 | this.dec_buffer.copyBytes(this.dec_buffer.bytes, overlap_pos, take); 433 | } else { 434 | this.dec_buffer.copyBytes(this.source.bytes, blkoff, take); 435 | } 436 | } 437 | }; 438 | 439 | /** 440 | * xref: xd3_decode_parse_halfinst 441 | * @param {!xd3_hinst} inst 442 | */ 443 | _XDelta3Decoder.prototype.xd3_decode_parse_halfinst = function(inst) { 444 | // Get size and address if necessary. 445 | if (inst.size == 0) { 446 | inst.size = this.inst_sect.getInteger(); 447 | } 448 | 449 | /* For copy instructions, read address. */ 450 | if (inst.type >= XD3_CPY) { 451 | var mode = inst.type - XD3_CPY; 452 | inst.addr = 453 | this.xd3_decode_address(this.dec_position, mode, this.addr_sect); 454 | } 455 | 456 | this.dec_position += inst.size; 457 | }; 458 | 459 | /** 460 | * xref: xd3_decode_instruction 461 | */ 462 | _XDelta3Decoder.prototype.xd3_decode_instruction = function() { 463 | var code_table = this.code_table; 464 | var instPair = this.inst_sect.getByte(); 465 | 466 | this.dec_current1.type = code_table.tableRows[instPair].type1; 467 | this.dec_current1.size = code_table.tableRows[instPair].size1; 468 | // dec_current1.addr keeps it previous value. 469 | 470 | this.dec_current2.type = code_table.tableRows[instPair].type2; 471 | this.dec_current2.size = code_table.tableRows[instPair].size2; 472 | // dec_current2.addr keeps it previous value. 473 | 474 | 475 | /* For each instruction with a real operation, decode the 476 | * corresponding size and addresses if necessary. Assume a 477 | * code-table may have NOOP in either position, although this is 478 | * unlikely. */ 479 | if (this.dec_current1.type != XD3_NOOP) { 480 | this.xd3_decode_parse_halfinst(this.dec_current1); 481 | } 482 | if (this.dec_current2.type != XD3_NOOP) { 483 | this.xd3_decode_parse_halfinst(this.dec_current2); 484 | } 485 | }; 486 | 487 | _XDelta3Decoder.prototype.xd3_decode_finish_window = function() { 488 | // stream->dec_winbytes = 0; 489 | // stream->dec_state = DEC_FINISH; 490 | this.data_sect.pos = 0; 491 | this.inst_sect.pos = 0; 492 | this.addr_sect.pos = 0; 493 | }; 494 | 495 | _XDelta3Decoder.prototype.xd3_decode_emit = function() { 496 | 497 | var instLength = this.inst_sect.bytes.byteLength; 498 | /* Decode next instruction pair. */ 499 | while (this.inst_sect.pos < instLength) { 500 | this.xd3_decode_instruction(); 501 | 502 | /* Output dec_current1 */ 503 | if (this.dec_current1.type != XD3_NOOP) { 504 | this.xd3_decode_output_halfinst(this.dec_current1); 505 | } 506 | /* Output dec_current2 */ 507 | if (this.dec_current2.type != XD3_NOOP) { 508 | this.xd3_decode_output_halfinst(this.dec_current2); 509 | } 510 | } 511 | if (this.dec_win_ind & VCD_ADLER32) { 512 | var a32 = adler32(1, this.dec_buffer.bytes, 0, this.dec_tgtlen); 513 | if (a32 != this.dec_adler32) { 514 | throw new Error('target window checksum mismatch'); 515 | } 516 | } 517 | 518 | /* Finished with a window. */ 519 | this.xd3_decode_finish_window(); 520 | }; 521 | 522 | _XDelta3Decoder.prototype.xd3_alloc = function(length) { 523 | return new Uint8Array(length); 524 | }; 525 | 526 | _XDelta3Decoder.prototype.xd3_decode_bytes = function(bytes, pos, length) { 527 | for (var i = 0; i < length; i++) { 528 | bytes[pos + i] = this.delta[this.position++]; 529 | } 530 | }; 531 | 532 | _XDelta3Decoder.prototype.xd3_decode_allocate = function(length) { 533 | var bytes = 534 | new Uint8Array(this.delta.slice(this.position, this.position + length)); 535 | this.position += length; 536 | return bytes; 537 | }; 538 | 539 | _XDelta3Decoder.prototype.getByte = function() { 540 | return this.delta[this.position++]; 541 | }; 542 | 543 | _XDelta3Decoder.prototype.getInteger = function() { 544 | var maxBytes = Math.min(20, this.delta.length - this.position); 545 | var integer = 0; 546 | for (var i = 0; i < maxBytes; i++) { 547 | var aPart = this.delta[this.position++]; 548 | integer += aPart & 0x7F; 549 | if (!(aPart & 0x80)) { 550 | return integer; 551 | } 552 | integer <<= 7; 553 | } 554 | throw new Error('delta integer too long'); 555 | }; 556 | 557 | /** 558 | * The code table. 559 | * @param {!Array} tableRows 560 | * @constructor 561 | * @struct 562 | */ 563 | var xd3_dinst_table = function(tableRows) { 564 | /** @type {!Array} */ 565 | this.tableRows = tableRows; 566 | }; 567 | 568 | /** 569 | * xd3_hinst 570 | * @constructor 571 | */ 572 | function xd3_hinst() { 573 | this.type = XD3_NOOP; 574 | this.size = 0; 575 | this.addr = 0; 576 | } 577 | 578 | /** 579 | * The code-table double instruction. 580 | * @constructor 581 | */ 582 | function xd3_dinst() { 583 | /** @type {number} */ 584 | this.type1 = XD3_NOOP; 585 | /** @type {number} */ 586 | this.size1 = 0; 587 | /** @type {number} */ 588 | this.type2 = XD3_NOOP; 589 | /** @type {number} */ 590 | this.size2 = 0; 591 | } 592 | 593 | /** 594 | * @param {!xd3_code_table_desc} desc 595 | * @return {!xd3_dinst_table} 596 | */ 597 | function xd3_build_code_table(desc) { 598 | var row = 0; 599 | var tableRows = new Array(256); 600 | for (var i = 0; i < 256; i++) { 601 | tableRows[i] = new xd3_dinst(); 602 | } 603 | var cpyModes = 2 + desc.near_modes + desc.same_modes; 604 | 605 | // The single RUN command. 606 | tableRows[row++].type1 = XD3_RUN; 607 | 608 | // The ADD only commands. 609 | tableRows[row++].type1 = XD3_ADD; 610 | for (var size1 = 1; size1 <= desc.add_sizes; size1++) { 611 | tableRows[row].type1 = XD3_ADD; 612 | tableRows[row++].size1 = size1; 613 | } 614 | 615 | // The Copy only commands. 616 | for (var mode = 0; mode < cpyModes; mode++) { 617 | tableRows[row++].type1 = XD3_CPY + mode; 618 | 619 | for (var size1 = MIN_MATCH; size1 < MIN_MATCH + desc.cpy_sizes; size1++) { 620 | tableRows[row].type1 = XD3_CPY + mode; 621 | tableRows[row++].size1 = size1; 622 | } 623 | } 624 | 625 | // The Add/Copy commands. 626 | for (var mode = 0; mode < cpyModes; mode++) { 627 | for (var size1 = 1; size1 <= desc.addcopy_add_max; size1++) { 628 | var max = (mode < 2 + desc.near_modes) ? // 629 | desc.addcopy_near_cpy_max : 630 | desc.addcopy_same_cpy_max; 631 | for (var size2 = MIN_MATCH; size2 <= max; size2++) { 632 | tableRows[row].type1 = XD3_ADD; 633 | tableRows[row].size1 = size1; 634 | tableRows[row].type2 = XD3_CPY + mode; 635 | tableRows[row++].size2 = size2; 636 | } 637 | } 638 | } 639 | 640 | // The Copy/Add commands. 641 | for (var mode = 0; mode < cpyModes; mode++) { 642 | var max = (mode < 2 + desc.near_modes) ? // 643 | desc.copyadd_near_cpy_max : 644 | desc.copyadd_same_cpy_max; 645 | for (var size1 = MIN_MATCH; size1 <= max; size1++) { 646 | for (var size2 = 1; size2 <= desc.copyadd_add_max; size2++) { 647 | tableRows[row].type1 = XD3_CPY + mode; 648 | tableRows[row].size1 = size1; 649 | tableRows[row].type2 = XD3_ADD; 650 | tableRows[row++].size2 = size2; 651 | } 652 | } 653 | } 654 | 655 | return new xd3_dinst_table(tableRows); 656 | } 657 | 658 | 659 | /** 660 | * @constructor 661 | */ 662 | function xd3_code_table_desc() { 663 | this.add_sizes = 0; 664 | this.near_modes = 0; 665 | this.same_modes = 0; 666 | this.cpy_sizes = 0; 667 | 668 | this.addcopy_add_max = 0; 669 | this.addcopy_near_cpy_max = 0; 670 | this.addcopy_same_cpy_max = 0; 671 | 672 | this.copyadd_add_max = 0; 673 | this.copyadd_near_cpy_max = 0; 674 | this.copyadd_same_cpy_max = 0; 675 | } 676 | 677 | 678 | /** 679 | * This builds the __rfc3284_code_table_desc 680 | * Assumes a single RUN instruction 681 | * Assumes that MIN_MATCH is 4. 682 | * @return {!xd3_code_table_desc} 683 | */ 684 | function build_rfc3284_code_table_desc() { 685 | var desc = new xd3_code_table_desc(); 686 | desc.add_sizes = 17; 687 | desc.near_modes = 4; 688 | desc.same_modes = 3; 689 | desc.cpy_sizes = 15; 690 | 691 | desc.addcopy_add_max = 4; 692 | desc.addcopy_near_cpy_max = 6; 693 | desc.addcopy_same_cpy_max = 4; 694 | 695 | desc.copyadd_add_max = 1; 696 | desc.copyadd_near_cpy_max = 4; 697 | desc.copyadd_same_cpy_max = 4; 698 | 699 | // xd3_code_table_sizes addcopy_max_sizes[MAX_MODES]; 700 | // { {6,163,3},{6,175,3},{6,187,3},{6,199,3},{6,211,3},{6,223,3}, 701 | // {4,235,1},{4,239,1},{4,243,1} }, 702 | 703 | // xd3_code_table_sizes copyadd_max_sizes[MAX_MODES]; 704 | // { {4,247,1},{4,248,1},{4,249,1},{4,250,1},{4,251,1},{4,252,1}, 705 | // {4,253,1},{4,254,1},{4,255,1} }, 706 | 707 | return desc; 708 | } 709 | 710 | var __rfc3284_code_table_desc = build_rfc3284_code_table_desc(); 711 | 712 | var A32_BASE = 65521; /* Largest prime smaller than 2^16 */ 713 | var A32_NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 714 | + (n+1)(BASE-1) <= 2^32-1 */ 715 | 716 | // 1140 #define A32_DO1(buf,i) {s1 += buf[i]; s2 += s1;} 717 | // 1141 #define A32_DO2(buf,i) A32_DO1(buf,i); A32_DO1(buf,i+1); 718 | // 1142 #define A32_DO4(buf,i) A32_DO2(buf,i); A32_DO2(buf,i+2); 719 | // 1143 #define A32_DO8(buf,i) A32_DO4(buf,i); A32_DO4(buf,i+4); 720 | // 1144 #define A32_DO16(buf) A32_DO8(buf,0); A32_DO8(buf,8); 721 | 722 | 723 | /** 724 | * Calculated the Adler32 checksum. 725 | * @param {number} adler I'm not sure what this is. 726 | * @param {!Uint8Array} buf 727 | * @param {number} pos 728 | * @param {number} len 729 | * @return {number} 730 | */ 731 | function adler32(adler, buf, pos, len) { 732 | var s1 = adler & 0xffff; 733 | var s2 = (adler >> 16) & 0xffff; 734 | var k; 735 | 736 | while (len > 0) { 737 | k = (len < A32_NMAX) ? len : A32_NMAX; 738 | len -= k; 739 | 740 | if (k != 0) { 741 | do { 742 | s1 += buf[pos++]; 743 | s2 += s1; 744 | } while (--k); 745 | } 746 | 747 | s1 %= A32_BASE; 748 | s2 %= A32_BASE; 749 | } 750 | 751 | return (s2 << 16) | s1; 752 | } 753 | 754 | 755 | /** 756 | * @constructor 757 | */ 758 | function xd3_addr_cache(s_near, s_same) { 759 | this.s_near = s_near; 760 | this.s_same = s_same; 761 | this.next_slot = 0; /* the circular index for near */ 762 | this.near_array = null; /* array of size s_near */ 763 | this.same_array = null; /* array of size s_same*256 */ 764 | } 765 | 766 | 767 | /** 768 | * @param {!xd3_addr_cache} acache 769 | */ 770 | function xd3_init_cache(acache) { 771 | if (acache.s_near > 0) { 772 | for (var i = 0; i < acache.near_array.length; i++) { 773 | acache.near_array[i] = 0; 774 | } 775 | acache.next_slot = 0; 776 | } 777 | 778 | if (acache.s_same > 0) { 779 | for (var i = 0; i < acache.same_array.length; i++) { 780 | acache.same_array[i] = 0; 781 | } 782 | } 783 | } 784 | 785 | /** 786 | * Used by the decoder to buffer input in sections. 787 | * XDelta3 C++ struct. 788 | * @constructor 789 | * @struct 790 | */ 791 | function xd3_desect() { 792 | /** 793 | * The buffer as a slice of the backingBuffer; 794 | * @type {?Uint8Array} 795 | */ 796 | this.bytes = null; 797 | 798 | /** @type {number} */ 799 | this.size = 0; 800 | 801 | /** @type {number} */ 802 | this.pos = 0; 803 | } 804 | 805 | /** 806 | * Gets a byte from the section. 807 | * @return {number} 808 | */ 809 | xd3_desect.prototype.getByte = function() { 810 | if (!this.bytes) { 811 | throw new Error('bytes not set'); 812 | } 813 | return this.bytes[this.pos++]; 814 | }; 815 | 816 | /** 817 | * Gets an integer from the section. 818 | * XDelta3 integers are encodes as a variable number of 7 bit bytes. Bit 8, the 819 | * most significant bit is used to indicate more bytes needed. 820 | * @return {number} 821 | */ 822 | xd3_desect.prototype.getInteger = function() { 823 | if (!this.bytes) { 824 | throw new Error('bytes not set'); 825 | } 826 | var val = 0; 827 | for (var i = 0; i < 10; i++) { 828 | var aByte = this.bytes[this.pos++]; 829 | val += aByte & 0x7F; 830 | if (!(aByte & 0x80)) { 831 | return val; 832 | } 833 | val <<= 7; 834 | } 835 | throw new Error('invalid number'); 836 | }; 837 | 838 | 839 | /** 840 | * Builds a default code table. 841 | * @return {!xd3_dinst_table} 842 | */ 843 | function xd3_rfc3284_code_table() { 844 | return xd3_build_code_table(__rfc3284_code_table_desc); 845 | } 846 | 847 | /** 848 | * Allocates and initializes a Javascript Array. 849 | * @return {!Array} 850 | */ 851 | function allocArray(len, val) { 852 | var arr = new Array(len); 853 | for (var i = 0; i < len; i++) { 854 | arr[i] = val; 855 | } 856 | return arr; 857 | } 858 | 859 | /** 860 | * @constructor 861 | */ 862 | function xd3_source() { 863 | /** @type {number} */ 864 | this.cpyoff_blkoff = -1; 865 | } 866 | 867 | /** 868 | * @param {!Uint8Array} bytes 869 | * @constructor 870 | */ 871 | function DataObject(bytes) { 872 | this.pos = 0; 873 | this.bytes = bytes; 874 | }; 875 | 876 | DataObject.prototype.getByte = function() { 877 | return this.bytes[this.pos++]; 878 | }; 879 | 880 | DataObject.prototype.getInteger = function() { 881 | var val = 0; 882 | for (var i = 0; i < 10; i++) { 883 | var aByte = this.bytes[this.pos++]; 884 | val += aByte & 0x7F; 885 | if (!(aByte & 0x80)) { 886 | return val; 887 | } 888 | val <<= 7; 889 | } 890 | throw new Error('invalid number'); 891 | }; 892 | 893 | DataObject.prototype.fill = function(val, length) { 894 | // TODO(bstell): see if there is a function for this. 895 | for (var i = 0; i < length; i++) { 896 | this.bytes[this.pos++] = val; 897 | } 898 | }; 899 | 900 | /** 901 | * @param {!xd3_desect} sect 902 | * @param {number} length 903 | */ 904 | DataObject.prototype.copySect = function(sect, length) { 905 | // TODO(bstell): see if there is a function for this. 906 | for (var i = 0; i < length; i++) { 907 | this.bytes[this.pos++] = sect.bytes[sect.pos++]; 908 | } 909 | }; 910 | 911 | DataObject.prototype.copyBytes = function(bytes, offset, length) { 912 | // TODO(bstell): see if there is a function for this. 913 | for (var i = 0; i < length; i++) { 914 | this.bytes[this.pos++] = bytes[offset++]; 915 | } 916 | }; 917 | 918 | })(); 919 | -------------------------------------------------------------------------------- /xdelta3_decoder_with_debug.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * xdelta3 - delta compression tools and library 4 | * Copyright 2016 Joshua MacDonald 5 | * 6 | * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 7 | * 2011, 2012, 2013, 2014, 2015 josh.macdonald@gmail.com 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | /** 23 | * @fileoverview This file implements XDelta3 decoding. 24 | * Note: the subroutine, method, field, and variable names do not follow 25 | * Javascript style guide but reflect the names in the XDelta3 C++ files. This 26 | * makes is to make it easier to keep this code in synch with the C++ code. 27 | * 28 | * The C++ code is very casual about initializing and accessing data structures. 29 | * This code is a port and follows that code style. 30 | */ 31 | 32 | (function() { 33 | 34 | // Check for namespace collision. 35 | if ((typeof window['XDelta3Decoder'] != 'undefined') 36 | || (typeof window.XDelta3Decoder != 'undefined')) { 37 | throw new Error('XDelta3Decoder already defined.'); 38 | } 39 | 40 | /** 41 | * The public class. 42 | */ 43 | window.XDelta3Decoder = function(debugOutput) { // 44 | }; 45 | 46 | var XDelta3Decoder = window.XDelta3Decoder; 47 | 48 | /** 49 | * The public API to decode a delta possibly with a source. 50 | * @param {!Uint8Array} delta The Xdelta delta file. 51 | * @param {Uint8Array=} opt_source The source file (optional). 52 | * @return {!ArrayBuffer} 53 | */ 54 | XDelta3Decoder.decode = function(delta, opt_source) { 55 | if (typeof opt_source != 'object') { 56 | opt_source = null; 57 | } 58 | var xdelta3 = new _XDelta3Decoder(delta, opt_source); 59 | var uint8Bytes = xdelta3.xd3_decode_input(); 60 | return uint8Bytes.buffer; 61 | } 62 | 63 | /** 64 | * The public API to disable debug printf code. 65 | */ 66 | XDelta3Decoder.disableDebug = function() { // DEBUG ONLY 67 | for (var i = 0; i < fallbackRoutines.length; i++) { // DEBUG ONLY 68 | var name = fallbackRoutines[i]; // DEBUG ONLY 69 | window[name] = function() {}; // DEBUG ONLY 70 | } // DEBUG ONLY 71 | } // DEBUG ONLY 72 | 73 | // Define debug fallback routines if needed. 74 | var fallbackRoutines = [ // DEBUG ONLY 75 | 'dumpBytes', // DEBUG ONLY 76 | 'dumpCodeTableRows', // DEBUG ONLY 77 | 'printf', // DEBUG ONLY 78 | 'printInstructionPair', // DEBUG ONLY 79 | 'toHexStr' // DEBUG ONLY 80 | ]; // DEBUG ONLY 81 | for (var i = 0; i < fallbackRoutines.length; i++) { // DEBUG ONLY 82 | var name = fallbackRoutines[i]; // DEBUG ONLY 83 | if (typeof window[name] == 'undefined') { // DEBUG ONLY 84 | window[name] = function() {}; // DEBUG ONLY 85 | } // DEBUG ONLY 86 | } // DEBUG ONLY 87 | 88 | /** 89 | * The XDelta3 data commands. 90 | */ 91 | /** @type {number} */ 92 | var XD3_NOOP = 0; 93 | /** @type {number} */ 94 | var XD3_ADD = 1; 95 | /** @type {number} */ 96 | var XD3_RUN = 2; 97 | /** @type {number} */ 98 | var XD3_CPY = 3; 99 | 100 | /** @type {number} */ 101 | var MIN_MATCH = 4; 102 | 103 | /** 104 | * Header indicator bits. 105 | */ 106 | /** @type {number} */ 107 | var VCD_SECONDARY = 1; 108 | /** @type {number} */ 109 | var VCD_CODETABLE = 2; 110 | /** @type {number} */ 111 | var VCD_APPHEADER = 4; 112 | /** @type {number} */ 113 | var VCD_INVHDR = ~(VCD_SECONDARY | VCD_CODETABLE | VCD_APPHEADER); 114 | 115 | var VCD_SOURCE = 0x01; 116 | var VCD_TARGET = 0x02; 117 | var VCD_ADLER32 = 0x04; 118 | 119 | 120 | /** 121 | * Declares the main decode class. 122 | * @param {!Uint8Array} delta The Xdelta3 delta file. 123 | * @param {Uint8Array=} opt_source The source file (optional). 124 | * @constructor 125 | */ 126 | function _XDelta3Decoder(delta, opt_source) { 127 | /** @type {!Uint8Array} */ 128 | this.delta = delta; 129 | 130 | var source = opt_source || new Uint8Array(1); 131 | /** @type {!DataObject} */ 132 | this.source = new DataObject(source); 133 | 134 | /** @type {!xd3_source} */ 135 | this.src = new xd3_source(); 136 | 137 | /** @type {number} */ 138 | this.position = 0; 139 | 140 | /** @type {number} */ 141 | this.dec_window_count = 0; 142 | 143 | /** @type {number} */ 144 | this.dec_winstart = 0; 145 | 146 | /** 147 | * The length of the target window. 148 | * @type {number} 149 | */ 150 | this.dec_tgtlen = 0; 151 | 152 | /** 153 | * The Alder32 checksum. This is used to verify the decoded bytes checksum 154 | * matches the checksum of the original. 155 | */ 156 | this.dec_adler32 = 0; 157 | 158 | /** 159 | * First half instruction. 160 | * @type {!xd3_hinst} 161 | */ 162 | this.dec_current1 = new xd3_hinst(); 163 | 164 | /** 165 | * Second half instruction. 166 | * @type {!xd3_hinst} 167 | */ 168 | this.dec_current2 = new xd3_hinst(); 169 | 170 | /** @type {!xd3_desect} */ 171 | this.data_sect = new xd3_desect(); 172 | 173 | /** @type {!xd3_desect} */ 174 | this.inst_sect = new xd3_desect(); 175 | 176 | /** @type {!xd3_desect} */ 177 | this.addr_sect = new xd3_desect(); 178 | 179 | /** 180 | * The address cache. 181 | * @type {!xd3_addr_cache} 182 | */ 183 | this.acache = new xd3_addr_cache( 184 | __rfc3284_code_table_desc.near_modes, 185 | __rfc3284_code_table_desc.same_modes); 186 | } 187 | 188 | /** 189 | * Allocates the address caches. 190 | */ 191 | _XDelta3Decoder.prototype.xd3_alloc_cache = function() { 192 | printf("xd3_alloc_cache\n"); // DEBUG ONLY 193 | this.acache.near_array = null; // not sure this is needed 194 | this.acache.same_array = null; // not sure this is needed 195 | if (this.acache.s_near > 0) { 196 | this.acache.near_array = allocArray(this.acache.s_near, 0); 197 | } 198 | if (this.acache.s_same > 0) { 199 | this.acache.same_array = allocArray(this.acache.s_same * 256, 0); 200 | } 201 | }; 202 | 203 | /** 204 | * Parses the delta file data and produces the targetWindow data. 205 | * @return {!Uint8Array} 206 | */ 207 | _XDelta3Decoder.prototype.xd3_decode_input = function() { 208 | printf("==================================\n"); // DEBUG ONLY 209 | printf(" HEADER pos = " + this.position + "\n"); // DEBUG ONLY 210 | printf("==================================\n"); // DEBUG ONLY 211 | printf("DEC_VCHEAD: load magic bytes\n"); // DEBUG ONLY 212 | dumpBytes(this.delta, 0, 4); // DEBUG ONLY 213 | 214 | printf("DEC_VCHEAD: check magic bytes\n"); // DEBUG ONLY 215 | if (this.delta[0] != 0xD6 || // 'V' with MSB set 216 | this.delta[1] != 0xC3 || // 'C' with MSB set 217 | this.delta[2] != 0xC4 || // 'D' with MSB set 218 | this.delta[3] != 0) { // unused but be set to zero 219 | throw new Error('XD3_INVALID_INPUT invalid magic'); 220 | } 221 | this.position = 4; 222 | 223 | this.dec_hdr_ind = this.delta[this.position++]; 224 | printf("DEC_HDRIND: dec_hdr_ind = " + toHexStr(this.dec_hdr_ind) + "\n"); // DEBUG ONLY 225 | if (this.dec_hdr_ind & VCD_INVHDR) { 226 | throw new Error('VCD_INVHDR unrecognized header indicator bits set'); 227 | } 228 | 229 | printf("DEC_SECONDID: read byte if VCD_SECONDARY(" + (this.dec_hdr_ind & VCD_SECONDARY) + ")\n"); // DEBUG ONLY 230 | if (this.dec_hdr_ind & VCD_SECONDARY) { 231 | throw new Error('VCD_SECONDARY not implemented'); 232 | } 233 | printf("DEC_TABLEN: read size if VCD_CODETABLE(" + (this.dec_hdr_ind & VCD_CODETABLE) + ")\n"); // DEBUG ONLY 234 | printf("DEC_NEAR: read byte if VCD_CODETABLE(" + (this.dec_hdr_ind & VCD_CODETABLE) + ")\n"); // DEBUG ONLY 235 | printf("DEC_SAME: read byte if VCD_CODETABLE(" + (this.dec_hdr_ind & VCD_CODETABLE) + ")\n"); // DEBUG ONLY 236 | 237 | if (this.dec_hdr_ind & VCD_CODETABLE) { 238 | throw new Error('VCD_CODETABLE support was removed'); 239 | } else { 240 | printf("use the default table.\n"); // DEBUG ONLY 241 | /* Use the default table. */ 242 | printf("use default table near_modes " + __rfc3284_code_table_desc.near_modes + "\n"); // DEBUG ONLY 243 | this.acache.s_near = __rfc3284_code_table_desc.near_modes; 244 | printf("use default table same_modes " + __rfc3284_code_table_desc.same_modes + "\n"); // DEBUG ONLY 245 | this.acache.s_same = __rfc3284_code_table_desc.same_modes; 246 | this.code_table = xd3_rfc3284_code_table(); 247 | } 248 | 249 | this.xd3_alloc_cache(); 250 | 251 | printf("DEC_APPLEN: read size if VCD_APPHEADER(" + (this.dec_hdr_ind & VCD_APPHEADER) // DEBUG ONLY 252 | + ")\n"); // DEBUG ONLY 253 | if (this.dec_hdr_ind & VCD_APPHEADER) { 254 | this.dec_appheadsz = this.getInteger(); 255 | printf("dec_appheadsz = " + this.dec_appheadsz + "\n"); // DEBUG ONLY 256 | printf("DEC_APPDAT: \n"); // DEBUG ONLY 257 | // Note: appHeader does not have a 0-termination. 258 | this.dec_apphead = this.xd3_alloc(this.dec_appheadsz + 1); 259 | this.xd3_decode_bytes(this.dec_apphead, 0, this.dec_appheadsz); 260 | this.dec_apphead[this.dec_appheadsz + 1] = 0; 261 | dumpBytes(this.dec_apphead, 0, this.dec_appheadsz + 1); // DEBUG ONLY 262 | } 263 | printf("pos after dec_appheader = " + this.position + "\n\n"); // DEBUG ONLY 264 | 265 | //var targetLength = 0; 266 | while (true) { 267 | printf("DEC_WININD\n"); // DEBUG ONLY 268 | printf("==================================\n"); // DEBUG ONLY 269 | printf(" WINDOW pos = "+this.position+"\n"); // DEBUG ONLY 270 | printf("==================================\n"); // DEBUG ONLY 271 | if (this.position >= this.delta.length) { 272 | break; 273 | } 274 | //targetLength += 275 | this.handleWindow(); 276 | } 277 | printf("no more data\n"); // DEBUG ONLY 278 | return this.dec_buffer.bytes; 279 | }; 280 | 281 | _XDelta3Decoder.prototype.xd3_decode_init_window = function() { 282 | this.dec_cpylen = 0; 283 | this.dec_cpyoff = 0; 284 | // this.dec_cksumbytes = 0; 285 | 286 | xd3_init_cache(this.acache); 287 | } 288 | 289 | _XDelta3Decoder.prototype.handleWindow = function() { 290 | this.dec_win_ind = this.delta[this.position++]; // DEC_WININD 291 | printf("dec_win_ind = " + this.dec_win_ind + "(" + toHexStr(this.dec_win_ind) + ")\n"); // DEBUG ONLY 292 | printf("dec_tgtlen = " + this.dec_tgtlen + "(" + toHexStr(this.dec_tgtlen) + ")\n"); // DEBUG ONLY 293 | 294 | if (this.dec_win_ind & ~7) { 295 | throw new Error('VCD_INVWIN unexpected bits set'); 296 | } 297 | 298 | printf("window_count = " + this.dec_window_count + "\n"); // DEBUG ONLY 299 | this.current_window = this.dec_window_count; 300 | 301 | this.dec_winstart += this.dec_tgtlen; 302 | printf("dec_winstart = " + this.dec_winstart + "\n"); // DEBUG ONLY 303 | 304 | printf('xd3_decode_init_window\n'); // DEBUG ONLY 305 | this.xd3_decode_init_window(); 306 | var SRCORTGT = VCD_SOURCE | VCD_TARGET; 307 | var srcortgt = SRCORTGT & this.dec_win_ind; 308 | printf("srcortgt = " + srcortgt + "\n"); // DEBUG ONLY 309 | 310 | // If using a source or target data segment: read the lenght and position 311 | // integers. 312 | printf("DEC_CPYLEN: dec_cpylen = "+this.dec_cpylen+"\n"); // DEBUG ONLY 313 | printf("DEC_CPYLEN: get size if SRCORTGT("+srcortgt+"), pos = "+this.position+"\n"); // DEBUG ONLY 314 | if (srcortgt) { 315 | this.dec_cpylen = this.getInteger(); // DEC_CPYLEN 316 | printf("dec_cpylen = " + this.dec_cpylen + "\n"); // DEBUG ONLY 317 | } 318 | this.dec_position = this.dec_cpylen; 319 | printf("DEC_CPYOFF: get size if SRCORTGT("+srcortgt+"), pos = "+this.position+"\n"); // DEBUG ONLY 320 | if (srcortgt) { 321 | var sourcePosition = this.getInteger(); // DEC_CPYOFF 322 | this.dec_cpyoff = sourcePosition; 323 | printf("dec_cpyoff = " + this.dec_cpyoff + "\n"); // DEBUG ONLY 324 | } 325 | 326 | this.dec_enclen = this.getInteger(); // DEC_ENCLEN 327 | printf("DEC_ENCLEN: dec_enclen = " + this.dec_enclen + "\n") // DEBUG ONLY 328 | 329 | // Calculate the position if the delta was actually read. 330 | // var positionAfterDelta = this.position + this.dec_enclen; 331 | 332 | // Get the target window length. 333 | this.dec_tgtlen = this.getInteger(); // DEC_TGTLEN 334 | printf("DEC_TGTLEN: dec_tgtlen = " + this.dec_tgtlen + "\n"); // DEBUG ONLY 335 | 336 | this.dec_del_ind = this.getByte(); // DEC_DELIND 337 | printf("DEC_DELIND: dec_del_ind = " + this.dec_del_ind + "\n"); // DEBUG ONLY 338 | 339 | this.data_sect.size = this.getInteger(); // DEC_DATALEN 340 | this.inst_sect.size = this.getInteger(); // DEC_INSTLEN 341 | this.addr_sect.size = this.getInteger(); // DEC_ADDRLEN 342 | printf("DEC_DATALEN: data_sect size = " + this.data_sect.size + "\n"); // DEBUG ONLY 343 | printf("DEC_INSTLEN: inst_sect size = " + this.inst_sect.size + "\n"); // DEBUG ONLY 344 | printf("DEC_ADDRLEN: addr_sect size = " + this.addr_sect.size + "\n"); // DEBUG ONLY 345 | 346 | printf("DEC_CKSUM: get checksum if VCD_ADLER32 pos = " + this.position + "\n"); // DEBUG ONLY 347 | if (this.dec_win_ind & VCD_ADLER32) { // DEC_CKSUM 348 | this.dec_cksum = this.xd3_decode_allocate(4); 349 | dumpBytes(this.dec_cksum, 0, 4); // DEBUG ONLY 350 | for (var i = 0; i < 4; i += 1) { 351 | this.dec_adler32 = (this.dec_adler32 << 8) | this.dec_cksum[i]; 352 | } 353 | printf("stream->dec_adler32 = "+this.dec_adler32+"\n"); // DEBUG ONLY 354 | } 355 | 356 | this.xd3_decode_sections(); 357 | dumpBytes(this.data_sect.bytes, 0, this.data_sect.size); // DEBUG ONLY 358 | dumpBytes(this.inst_sect.bytes, 0, this.inst_sect.size); // DEBUG ONLY 359 | dumpBytes(this.addr_sect.bytes, 0, this.addr_sect.size); // DEBUG ONLY 360 | 361 | printf('DEC_EMIT:\n'); // DEBUG ONLY 362 | /* In the C++ code: 363 | * To speed VCD_SOURCE block-address calculations, the source 364 | * cpyoff_blocks and cpyoff_blkoff are pre-computed. 365 | * However, in this Javascript code there is no 'blocks'. 366 | */ 367 | if (this.dec_win_ind & VCD_SOURCE) { 368 | printf("stream->dec_cpyoff = " + this.dec_cpyoff + "\n"); // DEBUG ONLY 369 | this.src.cpyoff_blkoff = this.dec_cpyoff; 370 | printf("src->cpyoff_blkoff = " + this.src.cpyoff_blkoff + "\n"); // DEBUG ONLY 371 | } 372 | this.xd3_decode_emit(); 373 | 374 | return this.dec_tgtlen; 375 | }; 376 | 377 | /** 378 | * This function only has code if the preprocessor statement 379 | * "#if SECONDARY_ANY" is set. SECONDARY_ANY does not seem to be set. 380 | */ 381 | _XDelta3Decoder.prototype.xd3_decode_secondary_sections = function() { // 382 | }; 383 | 384 | /** 385 | * @param {!xd3_desect} sect 386 | */ 387 | _XDelta3Decoder.prototype.xd3_decode_section = function(sect) { 388 | // It is possible to just point into the buffer but perhaps that can be done 389 | // later. 390 | sect.bytes = this.xd3_decode_allocate(sect.size); 391 | }; 392 | 393 | _XDelta3Decoder.prototype.xd3_decode_sections = function() { 394 | printf("xd3_decode_sections\n"); // DEBUG ONLY 395 | this.xd3_decode_section(this.data_sect); 396 | this.xd3_decode_section(this.inst_sect); 397 | this.xd3_decode_section(this.addr_sect); 398 | 399 | this.xd3_decode_secondary_sections(); 400 | 401 | this.xd3_decode_setup_buffers(); 402 | }; 403 | 404 | _XDelta3Decoder.prototype.xd3_decode_setup_buffers = function() { 405 | printf("xd3_decode_setup_buffers\n"); // DEBUG ONLY 406 | this.dec_buffer = new DataObject(new Uint8Array(this.dec_tgtlen)); 407 | }; 408 | 409 | var VCD_SELF = 0; 410 | var VCD_HERE = 1; 411 | 412 | /** 413 | * xd3_decode_address 414 | * @param {number} here 415 | * @param {number} mode 416 | * @param {!xd3_desect} sect 417 | */ 418 | _XDelta3Decoder.prototype.xd3_decode_address = function(here, mode, sect) { 419 | printf("xd3_decode_address\n"); // DEBUG ONLY 420 | var val; 421 | var same_start = 2 + this.acache.s_near; 422 | printf("here = " + here + "\n"); // DEBUG ONLY 423 | printf("mode = " + mode + "\n"); // DEBUG ONLY 424 | printf("acache.s_near = " + this.acache.s_near + "\n"); // DEBUG ONLY 425 | printf("same_start = " + same_start + "\n"); // DEBUG ONLY 426 | 427 | if (mode < same_start) { 428 | val = sect.getInteger(); 429 | printf("val = " + val + "\n"); // DEBUG ONLY 430 | switch (mode) { 431 | case VCD_SELF: 432 | printf('use self\n'); // DEBUG ONLY 433 | break; 434 | case VCD_HERE: 435 | printf('subtract from here\n'); // DEBUG ONLY 436 | // var old_val = val; 437 | val = here - val; 438 | printf("val = " + val + "\n"); // DEBUG ONLY 439 | break; 440 | default: 441 | printf('add near_array['+(mode-2)+'] = '+ // DEBUG ONLY 442 | this.acache.near_array[mode - 2]+'\n'); // DEBUG ONLY 443 | val += this.acache.near_array[mode - 2]; 444 | printf("val = " + val + "\n"); // DEBUG ONLY 445 | } 446 | } else { 447 | mode -= same_start; 448 | var offset = sect.getByte(); 449 | printf("mode = "+mode+", offset = "+offset+"\n"); // DEBUG ONLY 450 | val = this.acache.same_array[mode * 256 + offset]; 451 | printf("val = " + val + "\n"); // DEBUG ONLY 452 | } 453 | 454 | this.xd3_update_cache(this.acache, val); 455 | 456 | return val; 457 | }; 458 | 459 | /** 460 | * @param {!xd3_addr_cache} acache 461 | * @param {number} addr 462 | */ 463 | _XDelta3Decoder.prototype.xd3_update_cache = function(acache, addr) { 464 | printf("acache->s_near = "+acache.s_near+"\n"); // DEBUG ONLY 465 | if (acache.s_near > 0) { 466 | acache.near_array[acache.next_slot] = addr; 467 | printf("acache->near_array["+acache.next_slot+"] = "+addr+"\n"); // DEBUG ONLY 468 | acache.next_slot = (acache.next_slot + 1) % acache.s_near; 469 | printf("acache->next_slot = "+acache.next_slot+"\n"); // DEBUG ONLY 470 | } 471 | 472 | printf("acache->s_same = "+acache.s_same+"\n"); // DEBUG ONLY 473 | if (acache.s_same > 0) { 474 | acache.same_array[addr % (acache.s_same * 256)] = addr; 475 | printf("acache->same_array["+(addr % (acache.s_same*256))+"] = "+addr+"\n"); // DEBUG ONLY 476 | } 477 | }; 478 | 479 | /** 480 | * @param {!xd3_hinst} inst 481 | */ 482 | _XDelta3Decoder.prototype.xd3_decode_output_halfinst = function(inst) { 483 | printf("xd3_decode_output_halfinst: type=" + inst.type + // DEBUG ONLY 484 | ", addr=" + inst.addr + ", size=" + inst.size + "\n"); // DEBUG ONLY 485 | var take = inst.size; 486 | var blkoff; 487 | var start_pos = this.dec_buffer.pos; // DEBUG ONLY 488 | printf("start_pos = " + start_pos + "\n"); // DEBUG ONLY 489 | 490 | switch (inst.type) { 491 | case XD3_RUN: 492 | var val = this.data_sect.getByte(); 493 | printf(" >>>> XD3_RUN: memset "+ toHexStr(val) +" for " + take + "\n"); // DEBUG ONLY 494 | this.dec_buffer.fill(val, take); 495 | dumpBytes(this.dec_buffer.bytes, start_pos, take); // DEBUG ONLY 496 | break; 497 | 498 | case XD3_ADD: 499 | printf(" >>>> XD3_ADD: memcpy "+take+" from the data_sect\n"); // DEBUG ONLY 500 | this.dec_buffer.copySect(this.data_sect, take); 501 | dumpBytes(this.dec_buffer.bytes, start_pos, take); // DEBUG ONLY 502 | break; 503 | 504 | default: 505 | printf(" >>>> XD3_CPY\n"); // DEBUG ONLY 506 | printf("dec_cpylen = " + this.dec_cpylen + "\n"); // DEBUG ONLY 507 | var overlap; 508 | var overlap_pos; 509 | if (inst.addr < this.dec_cpylen) { 510 | overlap = 0; 511 | printf("overlap = 0\n"); // DEBUG ONLY 512 | if (this.dec_win_ind & VCD_TARGET) { 513 | throw new Error('VCD_TARGET not supported'); 514 | } else { 515 | printf("read source block\n"); // DEBUG ONLY 516 | blkoff = this.src.cpyoff_blkoff; 517 | printf("blkoff = " + blkoff + "\n"); // DEBUG ONLY 518 | printf("add inst addr to blkoff\n"); // DEBUG ONLY 519 | blkoff = this.dec_cpyoff + inst.addr; 520 | printf("blkoff = " + blkoff + "\n"); // DEBUG ONLY 521 | } 522 | } else { 523 | overlap = 1; 524 | printf("overlap = 1\n"); // DEBUG ONLY 525 | overlap_pos = inst.addr - this.dec_cpylen; 526 | printf("overlap_pos = "+overlap_pos+"\n"); // DEBUG ONLY 527 | } 528 | if (overlap) { 529 | printf(" <<<< manually copy "+take+"\n"); // DEBUG ONLY 530 | this.dec_buffer.copyBytes(this.dec_buffer.bytes, overlap_pos, take); 531 | dumpBytes(this.dec_buffer.bytes, start_pos, take); // DEBUG ONLY 532 | } else { 533 | printf(" <<<< memcopy take=" + take + "\n"); // DEBUG ONLY 534 | this.dec_buffer.copyBytes(this.source.bytes, blkoff, take); 535 | dumpBytes(this.dec_buffer.bytes, start_pos, take); // DEBUG ONLY 536 | } 537 | } 538 | }; 539 | 540 | /** 541 | * xref: xd3_decode_parse_halfinst 542 | * @param {!xd3_hinst} inst 543 | */ 544 | _XDelta3Decoder.prototype.xd3_decode_parse_halfinst = function(inst) { 545 | printf("xd3_decode_parse_halfinst\n"); // DEBUG ONLY 546 | // Get size and address if necessary. 547 | if (inst.size == 0) { 548 | inst.size = this.inst_sect.getInteger(); 549 | printf("read inst size = " + inst.size + "\n"); // DEBUG ONLY 550 | } 551 | 552 | /* For copy instructions, read address. */ 553 | if (inst.type >= XD3_CPY) { 554 | printf("dec_position = " + this.dec_position + "\n"); // DEBUG ONLY 555 | var mode = inst.type - XD3_CPY; 556 | printf("mode = " + mode + "\n"); // DEBUG ONLY 557 | inst.addr = 558 | this.xd3_decode_address(this.dec_position, mode, this.addr_sect); 559 | printf("XD3_CPY address = " + inst.addr + "\n"); // DEBUG ONLY 560 | } 561 | 562 | printf('dec_position = ' + this.dec_position + "\n"); // DEBUG ONLY 563 | printf('inst size = ' + inst.size + "\n"); // DEBUG ONLY 564 | this.dec_position += inst.size; 565 | printf('dec_position = ' + this.dec_position + "\n"); // DEBUG ONLY 566 | }; 567 | 568 | var instCount = 0; // DEBUG ONLY 569 | /** 570 | * xref: xd3_decode_instruction 571 | */ 572 | _XDelta3Decoder.prototype.xd3_decode_instruction = function() { 573 | printf("xd3_decode_instruction "+(instCount++)+"\n"); // DEBUG ONLY 574 | var code_table = this.code_table; 575 | var instPair = this.inst_sect.getByte(); 576 | printf('instPair = ' + instPair + "\n"); // DEBUG ONLY 577 | 578 | this.dec_current1.type = code_table.tableRows[instPair].type1; 579 | this.dec_current1.size = code_table.tableRows[instPair].size1; 580 | // dec_current1.addr keeps it previous value. 581 | 582 | this.dec_current2.type = code_table.tableRows[instPair].type2; 583 | this.dec_current2.size = code_table.tableRows[instPair].size2; 584 | // dec_current2.addr keeps it previous value. 585 | 586 | printInstructionPair(this.dec_current1, this.dec_current2); // DEBUG ONLY 587 | 588 | /* For each instruction with a real operation, decode the 589 | * corresponding size and addresses if necessary. Assume a 590 | * code-table may have NOOP in either position, although this is 591 | * unlikely. */ 592 | if (this.dec_current1.type != XD3_NOOP) { 593 | this.xd3_decode_parse_halfinst(this.dec_current1); 594 | } 595 | if (this.dec_current2.type != XD3_NOOP) { 596 | this.xd3_decode_parse_halfinst(this.dec_current2); 597 | } 598 | }; 599 | 600 | _XDelta3Decoder.prototype.xd3_decode_finish_window = function() { 601 | printf("xd3_decode_finish_window\n"); // DEBUG ONLY 602 | // stream->dec_winbytes = 0; 603 | // stream->dec_state = DEC_FINISH; 604 | this.data_sect.pos = 0; 605 | this.inst_sect.pos = 0; 606 | this.addr_sect.pos = 0; 607 | }; 608 | 609 | _XDelta3Decoder.prototype.xd3_decode_emit = function() { 610 | printf("\n\n"); // DEBUG ONLY 611 | printf("#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@\n"); // DEBUG ONLY 612 | printf(" xd3_decode_emit:\n"); // DEBUG ONLY 613 | printf("#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@\n"); // DEBUG ONLY 614 | 615 | var instLength = this.inst_sect.bytes.byteLength; 616 | /* Decode next instruction pair. */ 617 | while (this.inst_sect.pos < instLength) { 618 | printf('\n========== Decode next instruction pair ==========\n'); // DEBUG ONLY 619 | this.xd3_decode_instruction(); 620 | 621 | /* Output dec_current1 */ 622 | if (this.dec_current1.type != XD3_NOOP) { 623 | printf("output dec_current1 ----------\n"); // DEBUG ONLY 624 | this.xd3_decode_output_halfinst(this.dec_current1); 625 | } 626 | /* Output dec_current2 */ 627 | if (this.dec_current2.type != XD3_NOOP) { 628 | printf("output dec_current2 ----------\n"); // DEBUG ONLY 629 | this.xd3_decode_output_halfinst(this.dec_current2); 630 | } 631 | } 632 | printf("check checksum is VCD_ADLER32 set\n"); // DEBUG ONLY 633 | if (this.dec_win_ind & VCD_ADLER32) { 634 | var a32 = adler32(1, this.dec_buffer.bytes, 0, this.dec_tgtlen); 635 | printf("a32 = "+a32+"\n"); // DEBUG ONLY 636 | if (a32 != this.dec_adler32) { 637 | throw new Error('target window checksum mismatch'); 638 | } 639 | } 640 | 641 | /* Finished with a window. */ 642 | this.xd3_decode_finish_window(); 643 | }; 644 | 645 | _XDelta3Decoder.prototype.xd3_alloc = function(length) { 646 | return new Uint8Array(length); 647 | }; 648 | 649 | _XDelta3Decoder.prototype.xd3_decode_bytes = function(bytes, pos, length) { 650 | for (var i = 0; i < length; i++) { 651 | bytes[pos + i] = this.delta[this.position++]; 652 | } 653 | }; 654 | 655 | _XDelta3Decoder.prototype.xd3_decode_allocate = function(length) { 656 | var bytes = 657 | new Uint8Array(this.delta.slice(this.position, this.position + length)); 658 | this.position += length; 659 | return bytes; 660 | }; 661 | 662 | _XDelta3Decoder.prototype.getByte = function() { 663 | return this.delta[this.position++]; 664 | }; 665 | 666 | _XDelta3Decoder.prototype.getInteger = function() { 667 | var maxBytes = Math.min(20, this.delta.length - this.position); 668 | var integer = 0; 669 | for (var i = 0; i < maxBytes; i++) { 670 | var aPart = this.delta[this.position++]; 671 | integer += aPart & 0x7F; 672 | if (!(aPart & 0x80)) { 673 | return integer; 674 | } 675 | integer <<= 7; 676 | } 677 | throw new Error('delta integer too long'); 678 | }; 679 | 680 | /** 681 | * The code table. 682 | * @param {!Array} tableRows 683 | * @constructor 684 | * @struct 685 | */ 686 | var xd3_dinst_table = function(tableRows) { 687 | /** @type {!Array} */ 688 | this.tableRows = tableRows; 689 | }; 690 | 691 | /** 692 | * xd3_hinst 693 | * @constructor 694 | */ 695 | function xd3_hinst() { 696 | this.type = XD3_NOOP; 697 | this.size = 0; 698 | this.addr = 0; 699 | } 700 | 701 | /** 702 | * The code-table double instruction. 703 | * @constructor 704 | */ 705 | function xd3_dinst() { 706 | /** @type {number} */ 707 | this.type1 = XD3_NOOP; 708 | /** @type {number} */ 709 | this.size1 = 0; 710 | /** @type {number} */ 711 | this.type2 = XD3_NOOP; 712 | /** @type {number} */ 713 | this.size2 = 0; 714 | } 715 | 716 | /** 717 | * @param {!xd3_code_table_desc} desc 718 | * @return {!xd3_dinst_table} 719 | */ 720 | function xd3_build_code_table(desc) { 721 | printf("xd3_build_code_table\n"); // DEBUG ONLY 722 | var startRow = 0; // DEBUG ONLY 723 | var row = 0; 724 | var tableRows = new Array(256); 725 | for (var i = 0; i < 256; i++) { 726 | tableRows[i] = new xd3_dinst(); 727 | } 728 | var cpyModes = 2 + desc.near_modes + desc.same_modes; 729 | 730 | // The single RUN command. 731 | tableRows[row++].type1 = XD3_RUN; 732 | 733 | // The ADD only commands. 734 | tableRows[row++].type1 = XD3_ADD; 735 | for (var size1 = 1; size1 <= desc.add_sizes; size1++) { 736 | tableRows[row].type1 = XD3_ADD; 737 | tableRows[row++].size1 = size1; 738 | } 739 | dumpCodeTableRows(tableRows, startRow, row); // DEBUG ONLY 740 | startRow = row; // DEBUG ONLY 741 | 742 | // The Copy only commands. 743 | for (var mode = 0; mode < cpyModes; mode++) { 744 | tableRows[row++].type1 = XD3_CPY + mode; 745 | 746 | for (var size1 = MIN_MATCH; size1 < MIN_MATCH + desc.cpy_sizes; size1++) { 747 | tableRows[row].type1 = XD3_CPY + mode; 748 | tableRows[row++].size1 = size1; 749 | } 750 | dumpCodeTableRows(tableRows, startRow, row); // DEBUG ONLY 751 | startRow = row; // DEBUG ONLY 752 | } 753 | 754 | // The Add/Copy commands. 755 | for (var mode = 0; mode < cpyModes; mode++) { 756 | for (var size1 = 1; size1 <= desc.addcopy_add_max; size1++) { 757 | var max = (mode < 2 + desc.near_modes) ? // 758 | desc.addcopy_near_cpy_max : 759 | desc.addcopy_same_cpy_max; 760 | for (var size2 = MIN_MATCH; size2 <= max; size2++) { 761 | tableRows[row].type1 = XD3_ADD; 762 | tableRows[row].size1 = size1; 763 | tableRows[row].type2 = XD3_CPY + mode; 764 | tableRows[row++].size2 = size2; 765 | } 766 | } 767 | dumpCodeTableRows(tableRows, startRow, row); // DEBUG ONLY 768 | startRow = row; // DEBUG ONLY 769 | } 770 | 771 | // The Copy/Add commands. 772 | for (var mode = 0; mode < cpyModes; mode++) { 773 | var max = (mode < 2 + desc.near_modes) ? // 774 | desc.copyadd_near_cpy_max : 775 | desc.copyadd_same_cpy_max; 776 | for (var size1 = MIN_MATCH; size1 <= max; size1++) { 777 | for (var size2 = 1; size2 <= desc.copyadd_add_max; size2++) { 778 | tableRows[row].type1 = XD3_CPY + mode; 779 | tableRows[row].size1 = size1; 780 | tableRows[row].type2 = XD3_ADD; 781 | tableRows[row++].size2 = size2; 782 | } 783 | } 784 | } 785 | dumpCodeTableRows(tableRows, startRow, row); // DEBUG ONLY 786 | 787 | return new xd3_dinst_table(tableRows); 788 | } 789 | 790 | 791 | /** 792 | * @constructor 793 | */ 794 | function xd3_code_table_desc() { 795 | this.add_sizes = 0; 796 | this.near_modes = 0; 797 | this.same_modes = 0; 798 | this.cpy_sizes = 0; 799 | 800 | this.addcopy_add_max = 0; 801 | this.addcopy_near_cpy_max = 0; 802 | this.addcopy_same_cpy_max = 0; 803 | 804 | this.copyadd_add_max = 0; 805 | this.copyadd_near_cpy_max = 0; 806 | this.copyadd_same_cpy_max = 0; 807 | } 808 | 809 | 810 | /** 811 | * This builds the __rfc3284_code_table_desc 812 | * Assumes a single RUN instruction 813 | * Assumes that MIN_MATCH is 4. 814 | * @return {!xd3_code_table_desc} 815 | */ 816 | function build_rfc3284_code_table_desc() { 817 | var desc = new xd3_code_table_desc(); 818 | desc.add_sizes = 17; 819 | desc.near_modes = 4; 820 | desc.same_modes = 3; 821 | desc.cpy_sizes = 15; 822 | 823 | desc.addcopy_add_max = 4; 824 | desc.addcopy_near_cpy_max = 6; 825 | desc.addcopy_same_cpy_max = 4; 826 | 827 | desc.copyadd_add_max = 1; 828 | desc.copyadd_near_cpy_max = 4; 829 | desc.copyadd_same_cpy_max = 4; 830 | 831 | // xd3_code_table_sizes addcopy_max_sizes[MAX_MODES]; 832 | // { {6,163,3},{6,175,3},{6,187,3},{6,199,3},{6,211,3},{6,223,3}, 833 | // {4,235,1},{4,239,1},{4,243,1} }, 834 | 835 | // xd3_code_table_sizes copyadd_max_sizes[MAX_MODES]; 836 | // { {4,247,1},{4,248,1},{4,249,1},{4,250,1},{4,251,1},{4,252,1}, 837 | // {4,253,1},{4,254,1},{4,255,1} }, 838 | 839 | return desc; 840 | } 841 | 842 | var __rfc3284_code_table_desc = build_rfc3284_code_table_desc(); 843 | 844 | var A32_BASE = 65521; /* Largest prime smaller than 2^16 */ 845 | var A32_NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 846 | + (n+1)(BASE-1) <= 2^32-1 */ 847 | 848 | // 1140 #define A32_DO1(buf,i) {s1 += buf[i]; s2 += s1;} 849 | // 1141 #define A32_DO2(buf,i) A32_DO1(buf,i); A32_DO1(buf,i+1); 850 | // 1142 #define A32_DO4(buf,i) A32_DO2(buf,i); A32_DO2(buf,i+2); 851 | // 1143 #define A32_DO8(buf,i) A32_DO4(buf,i); A32_DO4(buf,i+4); 852 | // 1144 #define A32_DO16(buf) A32_DO8(buf,0); A32_DO8(buf,8); 853 | 854 | 855 | /** 856 | * Calculated the Adler32 checksum. 857 | * @param {number} adler I'm not sure what this is. 858 | * @param {!Uint8Array} buf 859 | * @param {number} pos 860 | * @param {number} len 861 | * @return {number} 862 | */ 863 | function adler32(adler, buf, pos, len) { 864 | printf("adler32: adler = "+adler+"\n"); // DEBUG ONLY 865 | printf("adler32: len = "+len+"\n"); // DEBUG ONLY 866 | var s1 = adler & 0xffff; 867 | var s2 = (adler >> 16) & 0xffff; 868 | var k; 869 | 870 | while (len > 0) { 871 | k = (len < A32_NMAX) ? len : A32_NMAX; 872 | len -= k; 873 | 874 | if (k != 0) { 875 | do { 876 | s1 += buf[pos++]; 877 | s2 += s1; 878 | } while (--k); 879 | } 880 | 881 | s1 %= A32_BASE; 882 | s2 %= A32_BASE; 883 | } 884 | 885 | return (s2 << 16) | s1; 886 | } 887 | 888 | 889 | /** 890 | * @constructor 891 | */ 892 | function xd3_addr_cache(s_near, s_same) { 893 | this.s_near = s_near; 894 | this.s_same = s_same; 895 | this.next_slot = 0; /* the circular index for near */ 896 | this.near_array = null; /* array of size s_near */ 897 | this.same_array = null; /* array of size s_same*256 */ 898 | } 899 | 900 | 901 | /** 902 | * @param {!xd3_addr_cache} acache 903 | */ 904 | function xd3_init_cache(acache) { 905 | printf("xd3_init_cache\n"); // DEBUG ONLY 906 | if (acache.s_near > 0) { 907 | for (var i = 0; i < acache.near_array.length; i++) { 908 | acache.near_array[i] = 0; 909 | } 910 | acache.next_slot = 0; 911 | } 912 | 913 | if (acache.s_same > 0) { 914 | for (var i = 0; i < acache.same_array.length; i++) { 915 | acache.same_array[i] = 0; 916 | } 917 | } 918 | } 919 | 920 | /** 921 | * Used by the decoder to buffer input in sections. 922 | * XDelta3 C++ struct. 923 | * @constructor 924 | * @struct 925 | */ 926 | function xd3_desect() { 927 | /** 928 | * The buffer as a slice of the backingBuffer; 929 | * @type {?Uint8Array} 930 | */ 931 | this.bytes = null; 932 | 933 | /** @type {number} */ 934 | this.size = 0; 935 | 936 | /** @type {number} */ 937 | this.pos = 0; 938 | } 939 | 940 | /** 941 | * Gets a byte from the section. 942 | * @return {number} 943 | */ 944 | xd3_desect.prototype.getByte = function() { 945 | if (!this.bytes) { 946 | throw new Error('bytes not set'); 947 | } 948 | return this.bytes[this.pos++]; 949 | }; 950 | 951 | /** 952 | * Gets an integer from the section. 953 | * XDelta3 integers are encodes as a variable number of 7 bit bytes. Bit 8, the 954 | * most significant bit is used to indicate more bytes needed. 955 | * @return {number} 956 | */ 957 | xd3_desect.prototype.getInteger = function() { 958 | if (!this.bytes) { 959 | throw new Error('bytes not set'); 960 | } 961 | var val = 0; 962 | for (var i = 0; i < 10; i++) { 963 | var aByte = this.bytes[this.pos++]; 964 | val += aByte & 0x7F; 965 | if (!(aByte & 0x80)) { 966 | return val; 967 | } 968 | val <<= 7; 969 | } 970 | throw new Error('invalid number'); 971 | }; 972 | 973 | 974 | /** 975 | * Builds a default code table. 976 | * @return {!xd3_dinst_table} 977 | */ 978 | function xd3_rfc3284_code_table() { 979 | printf("xd3_rfc3284_code_table\n"); // DEBUG ONLY 980 | return xd3_build_code_table(__rfc3284_code_table_desc); 981 | } 982 | 983 | /** 984 | * Allocates and initializes a Javascript Array. 985 | * @return {!Array} 986 | */ 987 | function allocArray(len, val) { 988 | var arr = new Array(len); 989 | for (var i = 0; i < len; i++) { 990 | arr[i] = val; 991 | } 992 | return arr; 993 | } 994 | 995 | /** 996 | * @constructor 997 | */ 998 | function xd3_source() { 999 | /** @type {number} */ 1000 | this.cpyoff_blkoff = -1; 1001 | } 1002 | 1003 | /** 1004 | * @param {!Uint8Array} bytes 1005 | * @constructor 1006 | */ 1007 | function DataObject(bytes) { 1008 | this.pos = 0; 1009 | this.bytes = bytes; 1010 | }; 1011 | 1012 | DataObject.prototype.getByte = function() { 1013 | return this.bytes[this.pos++]; 1014 | }; 1015 | 1016 | DataObject.prototype.getInteger = function() { 1017 | var val = 0; 1018 | for (var i = 0; i < 10; i++) { 1019 | var aByte = this.bytes[this.pos++]; 1020 | val += aByte & 0x7F; 1021 | if (!(aByte & 0x80)) { 1022 | return val; 1023 | } 1024 | val <<= 7; 1025 | } 1026 | throw new Error('invalid number'); 1027 | }; 1028 | 1029 | DataObject.prototype.fill = function(val, length) { 1030 | // TODO(bstell): see if there is a function for this. 1031 | for (var i = 0; i < length; i++) { 1032 | this.bytes[this.pos++] = val; 1033 | } 1034 | }; 1035 | 1036 | /** 1037 | * @param {!xd3_desect} sect 1038 | * @param {number} length 1039 | */ 1040 | DataObject.prototype.copySect = function(sect, length) { 1041 | // TODO(bstell): see if there is a function for this. 1042 | for (var i = 0; i < length; i++) { 1043 | this.bytes[this.pos++] = sect.bytes[sect.pos++]; 1044 | } 1045 | }; 1046 | 1047 | DataObject.prototype.copyBytes = function(bytes, offset, length) { 1048 | // TODO(bstell): see if there is a function for this. 1049 | for (var i = 0; i < length; i++) { 1050 | this.bytes[this.pos++] = bytes[offset++]; 1051 | } 1052 | }; 1053 | 1054 | })(); 1055 | --------------------------------------------------------------------------------