· 6 years ago · Sep 11, 2019, 07:30 AM
1const IPFS = require("ipfs")
2const xor = require('buffer-xor')
3const xorInplace = require('buffer-xor/inplace')
4const ipfs = new IPFS({
5 start: true,
6 EXPERIMENTAL: {
7 pubsub: true
8 }
9})
10async function testMain(ipfs) {
11 var ObjectDiff = new ObjectDiff();
12 var objA = {
13 subObjA: {
14 a: "fce00c604674c50b88fb958424ac3921 <-- padding xor compression test"
15 },
16 counterA: 25,
17 counterC: 5,
18 name: "test",
19 binary: Buffer.from("IPFS.exe has stopped working..."),
20 errors: null,
21 thisWillBeRemoved: 4,
22 array: [0, {b:"test"}]
23 };
24 var objB = {
25 subObjA: {
26 a: "fce00c604674c50b88fb958424ac3921 <-- padding (this should be the only difference)"
27 },
28 subObjB: {
29 title: "test."
30 },
31 counterA: 16,
32 counterB: 5,
33 name: "test",
34 binary: Buffer.from("IPFS.exe has stopped working..."),
35 errors: null,
36 array: [1,
37 {a:"test", b: "null"},
38 ]
39 };
40 var objAHash = await ipfs.dag.put(objA)
41 var objBHash = await ipfs.dag.put(objB);
42 var patch = dagDiff.rawDiff(objA, objB);
43 var patchHash = await ipfs.dag.put(patch);
44 //console.log(patchHash.toString())
45 //console.log(objA);
46 //console.log(Object.assign({}, objA))
47 console.log(patch);
48 console.log(dagDiff.apply(objA, patch))
49 //dagDiff.apply(objA, patch)
50}
51ipfs.on('ready', async () => {
52 testMain(ipfs);
53})
54
55class ObjectDiff {
56 //API multihash.
57 diff(original, changed) {
58 return new Promise(async (resolve, reject) => {
59 //TODO
60
61
62
63 })
64 }
65 bufferDiff(original, changed) {
66 if (!Buffer.isBuffer(original)) {
67 if (original === null || typeof original === "object" || typeof original === "number") {
68 //do not compare, just return value. (decompress if needed add later support)
69 return changed;
70 }
71 }
72 let tmp;
73 if (original.length > changed.length) {
74 tmp = original.slice(0, changed.length);
75 } else {
76 tmp = original;
77 }
78 //console.log(original)
79 return xor(tmp, changed);
80 }
81 valueDiff(original, changed) {
82 if (typeof original === "number") {
83 if (typeof changed === "number") {
84 return changed;
85 } else {
86 //if its binary, run binary diff, treat original as null
87 //return this.bufferDiff(null, changed); //Skip, let other following code handle this.
88 }
89 } else if (typeof changed === "number") {
90 return changed;
91 }
92 if (changed === null) {
93 return null;
94 }
95
96 if (!Buffer.isBuffer(original) && (typeof changed === "object" || typeof original === "object")) {
97 //recursive object diff. Not sure how this will work out for sub links.
98 //console.log(original)
99 return this.rawDiff(original, changed)
100 } else if (typeof changed === "object" && typeof original !== "object") {
101 return changed;
102 }
103 if (typeof changed === "string") {
104 //treat similar to binary.
105 //assume changed is a string too.
106
107 if (!Buffer.isBuffer(original)) {
108 //Original is not buffer
109 if (typeof original === "string") {
110 //Original is string
111 return this.bufferDiff(Buffer.from(original), Buffer.from(changed))
112 }
113 //assume anything else but buffer or string.
114
115 return this.bufferDiff(original, Buffer.from(changed)); //assume null, number, etc.
116 } else {
117 //assume buffer.
118
119
120 return this.bufferDiff(original, Buffer.from(changed))
121 }
122 }
123 if (Buffer.isBuffer(original) && Buffer.isBuffer(changed)) {
124 //both is buffer
125 return this.bufferDiff(original, changed)
126 } else if (Buffer.isBuffer(changed)) {
127 //only changed is buffer, original is other.
128 return this.bufferDiff(original, changed)
129 }
130
131 //console.log(original)
132 }
133 rawDiff(original, changed) {
134 var diff = {};
135
136 //finds if values of original exist in modified.
137 //what has been removed
138 for (var prop in original) {
139 //console.log(prop)
140
141 if (changed[prop]) {
142 if (changed[prop] !== original[prop]) {
143 //binary/value diff.
144
145 var diffval = this.valueDiff(original[prop], changed[prop]);
146 diff["~" + prop] = diffval;
147 //console.log(original[prop], changed[prop])
148 }
149 } else {
150 //removes keyvalue.
151 //output _keyname
152 diff["_" + prop] = null;
153 }
154 }
155 //reverse check,
156 //finds values that do not exist in original.
157 //what has been added
158 for (var prop in changed) {
159
160 if (!original[prop] && !diff["~" + prop]) {
161 //console.log(changed[prop])
162 //raw add
163 //output +keyname
164 diff["+" + prop] = changed[prop];
165 }
166 }
167 return diff;
168 }
169 applyValue(original, patch) {
170 if (typeof patch === "number") {
171 return patch;
172 }
173 if (patch === null) {
174 return null;
175 }
176 if (Buffer.isBuffer(patch)) {
177 if (Buffer.isBuffer(original)) {
178 let tmp;
179 if (original.length > patch.length) {
180 tmp = original.slice(0, patch.length);
181 } else {
182 tmp = original;
183 }
184
185 return xor(Buffer.from(tmp), patch);
186 } else if (typeof original === "string") {
187 //original is not a Buffer i.e string
188 let tmp;
189 if (original.length > patch.length) {
190 tmp = original.slice(0, patch.length);
191 } else {
192 tmp = original;
193 }
194 return xor(Buffer.from(tmp), patch).toString() //result should be string!
195 } else {
196
197 return patch;
198 }
199 }
200 }
201 //apply/patch, not designed to input circular objects.
202 apply(original, patch) {
203 var obj = {};
204 //console.log(original)
205 for (var key in original) {
206 if (original.hasOwnProperty(key)) {
207 obj[key] = original[key];
208 }
209 }
210
211 for (var prop in patch) {
212 var op = prop[0];
213 var shortname = prop.slice(1)
214 if (op === "_") {
215 delete obj[shortname]; //Remove element.
216 } else if (op === "+") {
217 obj[shortname] = patch[prop];
218 } else if (op === "~") {
219 if (typeof patch[prop] === "object" && !Buffer.isBuffer(patch[prop])) {
220 if (original[shortname] !== undefined) {
221 if(Array.isArray(original[shortname])) {
222 obj[shortname] = Object.values(this.apply(original[shortname], patch[prop]))
223 } else {
224 obj[shortname] = this.apply(original[shortname], patch[prop]) //recursive.
225 }
226 }
227 } else {
228 //obj = patch[prop];
229 obj[shortname] = this.applyValue(original[shortname], patch[prop]);
230 }
231 }
232 }
233 return obj;
234 }
235}