11import { b } from '@jsonjoy.com/util/lib/buffers/b' ;
2- import { toStr , toBin , diff , src , dst } from '../bin' ;
3- import { PATCH_OP_TYPE } from '../str' ;
2+ import { toStr , toBin , diff , src , dst , apply } from '../bin' ;
3+ import { PATCH_OP_TYPE , invert } from '../str' ;
44
55describe ( 'toHex()' , ( ) => {
66 test ( 'can convert buffer to string' , ( ) => {
@@ -14,6 +14,29 @@ describe('toHex()', () => {
1414 const hex = toStr ( buffer ) ;
1515 expect ( hex ) . toBe ( '\x00\x7f\xff' ) ;
1616 } ) ;
17+
18+ test ( 'handles empty buffer' , ( ) => {
19+ const buffer = b ( ) ;
20+ const hex = toStr ( buffer ) ;
21+ expect ( hex ) . toBe ( '' ) ;
22+ } ) ;
23+
24+ test ( 'handles single byte buffer' , ( ) => {
25+ const buffer = b ( 42 ) ;
26+ const hex = toStr ( buffer ) ;
27+ expect ( hex ) . toBe ( '\x2a' ) ;
28+ } ) ;
29+
30+ test ( 'handles all byte values' , ( ) => {
31+ const buffer = new Uint8Array ( 256 ) ;
32+ for ( let i = 0 ; i < 256 ; i ++ ) {
33+ buffer [ i ] = i ;
34+ }
35+ const hex = toStr ( buffer ) ;
36+ expect ( hex . length ) . toBe ( 256 ) ;
37+ expect ( hex . charCodeAt ( 0 ) ) . toBe ( 0 ) ;
38+ expect ( hex . charCodeAt ( 255 ) ) . toBe ( 255 ) ;
39+ } ) ;
1740} ) ;
1841
1942describe ( 'fromHex()' , ( ) => {
@@ -26,6 +49,23 @@ describe('fromHex()', () => {
2649 const buffer = toBin ( '\x00\x7f\xff' ) ;
2750 expect ( buffer ) . toEqual ( b ( 0 , 127 , 255 ) ) ;
2851 } ) ;
52+
53+ test ( 'handles empty string' , ( ) => {
54+ const buffer = toBin ( '' ) ;
55+ expect ( buffer ) . toEqual ( b ( ) ) ;
56+ } ) ;
57+
58+ test ( 'handles single character' , ( ) => {
59+ const buffer = toBin ( '\x2a' ) ;
60+ expect ( buffer ) . toEqual ( b ( 42 ) ) ;
61+ } ) ;
62+
63+ test ( 'round-trip conversion' , ( ) => {
64+ const originalBuffer = b ( 0 , 1 , 127 , 128 , 254 , 255 ) ;
65+ const hex = toStr ( originalBuffer ) ;
66+ const convertedBuffer = toBin ( hex ) ;
67+ expect ( convertedBuffer ) . toEqual ( originalBuffer ) ;
68+ } ) ;
2969} ) ;
3070
3171describe ( 'diff()' , ( ) => {
@@ -66,4 +106,221 @@ describe('diff()', () => {
66106 expect ( src ( patch1 ) ) . toEqual ( b ( 1 , 2 , 3 ) ) ;
67107 expect ( dst ( patch1 ) ) . toEqual ( b ( 2 , 3 , 4 ) ) ;
68108 } ) ;
109+
110+ test ( 'handles empty buffers' , ( ) => {
111+ const patch1 = diff ( b ( ) , b ( 1 , 2 , 3 ) ) ;
112+ expect ( patch1 ) . toEqual ( [ [ PATCH_OP_TYPE . INS , toStr ( b ( 1 , 2 , 3 ) ) ] ] ) ;
113+ expect ( src ( patch1 ) ) . toEqual ( b ( ) ) ;
114+ expect ( dst ( patch1 ) ) . toEqual ( b ( 1 , 2 , 3 ) ) ;
115+
116+ const patch2 = diff ( b ( 1 , 2 , 3 ) , b ( ) ) ;
117+ expect ( patch2 ) . toEqual ( [ [ PATCH_OP_TYPE . DEL , toStr ( b ( 1 , 2 , 3 ) ) ] ] ) ;
118+ expect ( src ( patch2 ) ) . toEqual ( b ( 1 , 2 , 3 ) ) ;
119+ expect ( dst ( patch2 ) ) . toEqual ( b ( ) ) ;
120+
121+ const patch3 = diff ( b ( ) , b ( ) ) ;
122+ expect ( patch3 ) . toEqual ( [ ] ) ;
123+ expect ( src ( patch3 ) ) . toEqual ( b ( ) ) ;
124+ expect ( dst ( patch3 ) ) . toEqual ( b ( ) ) ;
125+ } ) ;
126+
127+ test ( 'handles null bytes' , ( ) => {
128+ const patch1 = diff ( b ( 0 , 0 , 0 ) , b ( 0 , 1 , 0 ) ) ;
129+ expect ( src ( patch1 ) ) . toEqual ( b ( 0 , 0 , 0 ) ) ;
130+ expect ( dst ( patch1 ) ) . toEqual ( b ( 0 , 1 , 0 ) ) ;
131+
132+ const patch2 = diff ( b ( 1 , 2 , 3 ) , b ( 0 , 1 , 2 , 3 , 0 ) ) ;
133+ expect ( src ( patch2 ) ) . toEqual ( b ( 1 , 2 , 3 ) ) ;
134+ expect ( dst ( patch2 ) ) . toEqual ( b ( 0 , 1 , 2 , 3 , 0 ) ) ;
135+ } ) ;
136+
137+ test ( 'handles maximum byte values' , ( ) => {
138+ const patch1 = diff ( b ( 255 , 255 ) , b ( 255 , 254 , 255 ) ) ;
139+ expect ( src ( patch1 ) ) . toEqual ( b ( 255 , 255 ) ) ;
140+ expect ( dst ( patch1 ) ) . toEqual ( b ( 255 , 254 , 255 ) ) ;
141+
142+ const patch2 = diff ( b ( 0 , 255 ) , b ( 255 , 0 ) ) ;
143+ expect ( src ( patch2 ) ) . toEqual ( b ( 0 , 255 ) ) ;
144+ expect ( dst ( patch2 ) ) . toEqual ( b ( 255 , 0 ) ) ;
145+ } ) ;
146+
147+ test ( 'handles repetitive binary patterns' , ( ) => {
148+ const pattern1 = b ( 170 , 170 , 170 , 170 ) ; // 10101010 pattern
149+ const pattern2 = b ( 170 , 170 , 85 , 170 ) ; // 10101010, 10101010, 01010101, 10101010
150+ const patch = diff ( pattern1 , pattern2 ) ;
151+ expect ( src ( patch ) ) . toEqual ( pattern1 ) ;
152+ expect ( dst ( patch ) ) . toEqual ( pattern2 ) ;
153+
154+ const alternating1 = b ( 1 , 0 , 1 , 0 , 1 , 0 ) ;
155+ const alternating2 = b ( 1 , 0 , 1 , 1 , 1 , 0 ) ;
156+ const patch2 = diff ( alternating1 , alternating2 ) ;
157+ expect ( src ( patch2 ) ) . toEqual ( alternating1 ) ;
158+ expect ( dst ( patch2 ) ) . toEqual ( alternating2 ) ;
159+ } ) ;
160+
161+ test ( 'handles large binary differences' , ( ) => {
162+ const large1 = new Uint8Array ( 100 ) . fill ( 42 ) ;
163+ const large2 = new Uint8Array ( 100 ) . fill ( 43 ) ;
164+ const patch = diff ( large1 , large2 ) ;
165+ expect ( src ( patch ) ) . toEqual ( large1 ) ;
166+ expect ( dst ( patch ) ) . toEqual ( large2 ) ;
167+ } ) ;
168+
169+ test ( 'handles single byte arrays' , ( ) => {
170+ const patch1 = diff ( b ( 1 ) , b ( 2 ) ) ;
171+ expect ( src ( patch1 ) ) . toEqual ( b ( 1 ) ) ;
172+ expect ( dst ( patch1 ) ) . toEqual ( b ( 2 ) ) ;
173+
174+ const patch2 = diff ( b ( 0 ) , b ( 255 ) ) ;
175+ expect ( src ( patch2 ) ) . toEqual ( b ( 0 ) ) ;
176+ expect ( dst ( patch2 ) ) . toEqual ( b ( 255 ) ) ;
177+ } ) ;
178+ } ) ;
179+
180+ describe ( 'apply()' , ( ) => {
181+ test ( 'applies binary patches correctly' , ( ) => {
182+ const src1 = b ( 1 , 2 , 3 , 4 , 5 ) ;
183+ const dst1 = b ( 1 , 0 , 3 , 4 , 6 ) ;
184+ const patch = diff ( src1 , dst1 ) ;
185+
186+ const result = src1 . slice ( ) ; // copy
187+ const insertions : { pos : number ; data : Uint8Array } [ ] = [ ] ;
188+ const deletions : { pos : number ; len : number } [ ] = [ ] ;
189+
190+ apply (
191+ patch ,
192+ result . length ,
193+ ( pos , data ) => {
194+ insertions . push ( { pos, data} ) ;
195+ } ,
196+ ( pos , len ) => {
197+ deletions . push ( { pos, len} ) ;
198+ } ,
199+ ) ;
200+
201+ // Apply deletions and insertions to verify the logic works
202+ // (Note: this is just testing the callback mechanism)
203+ expect ( insertions . length + deletions . length ) . toBeGreaterThan ( 0 ) ;
204+ } ) ;
205+
206+ test ( 'handles empty buffer patches' , ( ) => {
207+ const patch1 = diff ( b ( ) , b ( 1 , 2 , 3 ) ) ;
208+ let insertCount = 0 ;
209+ let deleteCount = 0 ;
210+
211+ apply (
212+ patch1 ,
213+ 0 ,
214+ ( pos , data ) => {
215+ insertCount ++ ;
216+ expect ( data ) . toEqual ( b ( 1 , 2 , 3 ) ) ;
217+ expect ( pos ) . toBe ( 0 ) ;
218+ } ,
219+ ( pos , len ) => {
220+ deleteCount ++ ;
221+ } ,
222+ ) ;
223+
224+ expect ( insertCount ) . toBe ( 1 ) ;
225+ expect ( deleteCount ) . toBe ( 0 ) ;
226+ } ) ;
227+ } ) ;
228+
229+ describe ( 'Binary edge cases and stress tests' , ( ) => {
230+ test ( 'handles binary data that looks like text' , ( ) => {
231+ // Binary data that coincidentally forms valid UTF-8
232+ const text1 = new TextEncoder ( ) . encode ( 'Hello World' ) ;
233+ const text2 = new TextEncoder ( ) . encode ( 'Hello Universe' ) ;
234+
235+ const patch = diff ( text1 , text2 ) ;
236+ expect ( src ( patch ) ) . toEqual ( text1 ) ;
237+ expect ( dst ( patch ) ) . toEqual ( text2 ) ;
238+ } ) ;
239+
240+ test ( 'handles large binary arrays efficiently' , ( ) => {
241+ const large1 = new Uint8Array ( 1000 ) ;
242+ const large2 = new Uint8Array ( 1000 ) ;
243+
244+ // Fill with different patterns
245+ for ( let i = 0 ; i < 1000 ; i ++ ) {
246+ large1 [ i ] = i % 256 ;
247+ large2 [ i ] = ( i + 1 ) % 256 ;
248+ }
249+
250+ const startTime = Date . now ( ) ;
251+ const patch = diff ( large1 , large2 ) ;
252+ const endTime = Date . now ( ) ;
253+
254+ expect ( endTime - startTime ) . toBeLessThan ( 1000 ) ; // Should be fast
255+ expect ( src ( patch ) ) . toEqual ( large1 ) ;
256+ expect ( dst ( patch ) ) . toEqual ( large2 ) ;
257+ } ) ;
258+
259+ test ( 'handles binary patterns with many repetitions' , ( ) => {
260+ const pattern1 = new Uint8Array ( 100 ) ;
261+ const pattern2 = new Uint8Array ( 100 ) ;
262+
263+ // Create repetitive patterns
264+ for ( let i = 0 ; i < 100 ; i ++ ) {
265+ pattern1 [ i ] = i % 4 ; // 0,1,2,3,0,1,2,3...
266+ pattern2 [ i ] = ( i + 1 ) % 4 ; // 1,2,3,0,1,2,3,0...
267+ }
268+
269+ const patch = diff ( pattern1 , pattern2 ) ;
270+ expect ( src ( patch ) ) . toEqual ( pattern1 ) ;
271+ expect ( dst ( patch ) ) . toEqual ( pattern2 ) ;
272+ } ) ;
273+
274+ test ( 'handles binary inversion' , ( ) => {
275+ const buf1 = b ( 1 , 2 , 3 , 4 , 5 ) ;
276+ const buf2 = b ( 1 , 0 , 3 , 4 , 6 ) ;
277+
278+ const patch = diff ( buf1 , buf2 ) ;
279+ const inverted = invert ( patch ) ;
280+
281+ // Inverted patch should transform buf2 back to buf1
282+ expect ( src ( inverted ) ) . toEqual ( buf2 ) ;
283+ expect ( dst ( inverted ) ) . toEqual ( buf1 ) ;
284+ } ) ;
285+
286+ test ( 'handles mixed null and non-null bytes' , ( ) => {
287+ const mixed1 = b ( 0 , 255 , 0 , 128 , 0 ) ;
288+ const mixed2 = b ( 255 , 0 , 255 , 0 , 255 ) ;
289+
290+ const patch = diff ( mixed1 , mixed2 ) ;
291+ expect ( src ( patch ) ) . toEqual ( mixed1 ) ;
292+ expect ( dst ( patch ) ) . toEqual ( mixed2 ) ;
293+ } ) ;
294+
295+ test ( 'validates conversion consistency across operations' , ( ) => {
296+ const testBuffers = [
297+ b ( ) ,
298+ b ( 0 ) ,
299+ b ( 255 ) ,
300+ b ( 0 , 255 ) ,
301+ b ( 255 , 0 ) ,
302+ b ( 1 , 2 , 3 , 4 , 5 ) ,
303+ new Uint8Array ( 256 ) . map ( ( _ , i ) => i ) , // All possible byte values
304+ ] ;
305+
306+ for ( let i = 0 ; i < testBuffers . length ; i ++ ) {
307+ for ( let j = 0 ; j < testBuffers . length ; j ++ ) {
308+ if ( i === j ) continue ;
309+
310+ const buf1 = testBuffers [ i ] ;
311+ const buf2 = testBuffers [ j ] ;
312+ const patch = diff ( buf1 , buf2 ) ;
313+
314+ // Verify conversion consistency
315+ expect ( src ( patch ) ) . toEqual ( buf1 ) ;
316+ expect ( dst ( patch ) ) . toEqual ( buf2 ) ;
317+
318+ // Verify round-trip conversion
319+ const str1 = toStr ( buf1 ) ;
320+ const str2 = toStr ( buf2 ) ;
321+ expect ( toBin ( str1 ) ) . toEqual ( buf1 ) ;
322+ expect ( toBin ( str2 ) ) . toEqual ( buf2 ) ;
323+ }
324+ }
325+ } ) ;
69326} ) ;
0 commit comments