pax_global_header00006660000000000000000000000064127440506560014523gustar00rootroot0000000000000052 comment=3035450ff8b987ec4373f65c40767f096b35f2d2 binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/000077500000000000000000000000001274405065600204205ustar00rootroot00000000000000binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/.gitignore000066400000000000000000000000071274405065600224050ustar00rootroot00000000000000test.* binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/License000066400000000000000000000020341274405065600217240ustar00rootroot00000000000000Copyright 2012 Keith Rarick Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/Readme.md000066400000000000000000000003661274405065600221440ustar00rootroot00000000000000# binarydist Package binarydist implements binary diff and patch as described on . It reads and writes files compatible with the tools there. Documentation at . binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/bzip2.go000066400000000000000000000012711274405065600217760ustar00rootroot00000000000000package binarydist import ( "io" "os/exec" ) type bzip2Writer struct { c *exec.Cmd w io.WriteCloser } func (w bzip2Writer) Write(b []byte) (int, error) { return w.w.Write(b) } func (w bzip2Writer) Close() error { if err := w.w.Close(); err != nil { return err } return w.c.Wait() } // Package compress/bzip2 implements only decompression, // so we'll fake it by running bzip2 in another process. func newBzip2Writer(w io.Writer) (wc io.WriteCloser, err error) { var bw bzip2Writer bw.c = exec.Command("bzip2", "-c") bw.c.Stdout = w if bw.w, err = bw.c.StdinPipe(); err != nil { return nil, err } if err = bw.c.Start(); err != nil { return nil, err } return bw, nil } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/common_test.go000066400000000000000000000022061274405065600232760ustar00rootroot00000000000000package binarydist import ( "io" "io/ioutil" "math/rand" "os" ) func mustOpen(path string) *os.File { f, err := os.Open(path) if err != nil { panic(err) } return f } func mustReadAll(r io.Reader) []byte { b, err := ioutil.ReadAll(r) if err != nil { panic(err) } return b } func fileCmp(a, b *os.File) int64 { sa, err := a.Seek(0, 2) if err != nil { panic(err) } sb, err := b.Seek(0, 2) if err != nil { panic(err) } if sa != sb { return sa } _, err = a.Seek(0, 0) if err != nil { panic(err) } _, err = b.Seek(0, 0) if err != nil { panic(err) } pa, err := ioutil.ReadAll(a) if err != nil { panic(err) } pb, err := ioutil.ReadAll(b) if err != nil { panic(err) } for i := range pa { if pa[i] != pb[i] { return int64(i) } } return -1 } func mustWriteRandFile(path string, size int, seed int64) *os.File { p := make([]byte, size) rand.Seed(seed) _, err := rand.Read(p) if err != nil { panic(err) } f, err := os.Create(path) if err != nil { panic(err) } _, err = f.Write(p) if err != nil { panic(err) } _, err = f.Seek(0, 0) if err != nil { panic(err) } return f } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/diff.go000066400000000000000000000157561274405065600216750ustar00rootroot00000000000000package binarydist import ( "bytes" "encoding/binary" "io" "io/ioutil" ) func swap(a []int, i, j int) { a[i], a[j] = a[j], a[i] } func split(I, V []int, start, length, h int) { var i, j, k, x, jj, kk int if length < 16 { for k = start; k < start+length; k += j { j = 1 x = V[I[k]+h] for i = 1; k+i < start+length; i++ { if V[I[k+i]+h] < x { x = V[I[k+i]+h] j = 0 } if V[I[k+i]+h] == x { swap(I, k+i, k+j) j++ } } for i = 0; i < j; i++ { V[I[k+i]] = k + j - 1 } if j == 1 { I[k] = -1 } } return } x = V[I[start+length/2]+h] jj = 0 kk = 0 for i = start; i < start+length; i++ { if V[I[i]+h] < x { jj++ } if V[I[i]+h] == x { kk++ } } jj += start kk += jj i = start j = 0 k = 0 for i < jj { if V[I[i]+h] < x { i++ } else if V[I[i]+h] == x { swap(I, i, jj+j) j++ } else { swap(I, i, kk+k) k++ } } for jj+j < kk { if V[I[jj+j]+h] == x { j++ } else { swap(I, jj+j, kk+k) k++ } } if jj > start { split(I, V, start, jj-start, h) } for i = 0; i < kk-jj; i++ { V[I[jj+i]] = kk - 1 } if jj == kk-1 { I[jj] = -1 } if start+length > kk { split(I, V, kk, start+length-kk, h) } } func qsufsort(obuf []byte) []int { var buckets [256]int var i, h int I := make([]int, len(obuf)+1) V := make([]int, len(obuf)+1) for _, c := range obuf { buckets[c]++ } for i = 1; i < 256; i++ { buckets[i] += buckets[i-1] } copy(buckets[1:], buckets[:]) buckets[0] = 0 for i, c := range obuf { buckets[c]++ I[buckets[c]] = i } I[0] = len(obuf) for i, c := range obuf { V[i] = buckets[c] } V[len(obuf)] = 0 for i = 1; i < 256; i++ { if buckets[i] == buckets[i-1]+1 { I[buckets[i]] = -1 } } I[0] = -1 for h = 1; I[0] != -(len(obuf) + 1); h += h { var n int for i = 0; i < len(obuf)+1; { if I[i] < 0 { n -= I[i] i -= I[i] } else { if n != 0 { I[i-n] = -n } n = V[I[i]] + 1 - i split(I, V, i, n, h) i += n n = 0 } } if n != 0 { I[i-n] = -n } } for i = 0; i < len(obuf)+1; i++ { I[V[i]] = i } return I } func matchlen(a, b []byte) (i int) { for i < len(a) && i < len(b) && a[i] == b[i] { i++ } return i } func search(I []int, obuf, nbuf []byte, st, en int) (pos, n int) { if en-st < 2 { x := matchlen(obuf[I[st]:], nbuf) y := matchlen(obuf[I[en]:], nbuf) if x > y { return I[st], x } else { return I[en], y } } x := st + (en-st)/2 if bytes.Compare(obuf[I[x]:], nbuf) < 0 { return search(I, obuf, nbuf, x, en) } else { return search(I, obuf, nbuf, st, x) } panic("unreached") } // Diff computes the difference between old and new, according to the bsdiff // algorithm, and writes the result to patch. func Diff(old, new io.Reader, patch io.Writer) error { obuf, err := ioutil.ReadAll(old) if err != nil { return err } nbuf, err := ioutil.ReadAll(new) if err != nil { return err } pbuf, err := diffBytes(obuf, nbuf) if err != nil { return err } _, err = patch.Write(pbuf) return err } func diffBytes(obuf, nbuf []byte) ([]byte, error) { var patch seekBuffer err := diff(obuf, nbuf, &patch) if err != nil { return nil, err } return patch.buf, nil } func diff(obuf, nbuf []byte, patch io.WriteSeeker) error { var lenf int I := qsufsort(obuf) db := make([]byte, len(nbuf)) eb := make([]byte, len(nbuf)) var dblen, eblen int var hdr header hdr.Magic = magic hdr.NewSize = int64(len(nbuf)) err := binary.Write(patch, signMagLittleEndian{}, &hdr) if err != nil { return err } // Compute the differences, writing ctrl as we go pfbz2, err := newBzip2Writer(patch) if err != nil { return err } var scan, pos, length int var lastscan, lastpos, lastoffset int for scan < len(nbuf) { var oldscore int scan += length for scsc := scan; scan < len(nbuf); scan++ { pos, length = search(I, obuf, nbuf[scan:], 0, len(obuf)) for ; scsc < scan+length; scsc++ { if scsc+lastoffset < len(obuf) && obuf[scsc+lastoffset] == nbuf[scsc] { oldscore++ } } if (length == oldscore && length != 0) || length > oldscore+8 { break } if scan+lastoffset < len(obuf) && obuf[scan+lastoffset] == nbuf[scan] { oldscore-- } } if length != oldscore || scan == len(nbuf) { var s, Sf int lenf = 0 for i := 0; lastscan+i < scan && lastpos+i < len(obuf); { if obuf[lastpos+i] == nbuf[lastscan+i] { s++ } i++ if s*2-i > Sf*2-lenf { Sf = s lenf = i } } lenb := 0 if scan < len(nbuf) { var s, Sb int for i := 1; (scan >= lastscan+i) && (pos >= i); i++ { if obuf[pos-i] == nbuf[scan-i] { s++ } if s*2-i > Sb*2-lenb { Sb = s lenb = i } } } if lastscan+lenf > scan-lenb { overlap := (lastscan + lenf) - (scan - lenb) s := 0 Ss := 0 lens := 0 for i := 0; i < overlap; i++ { if nbuf[lastscan+lenf-overlap+i] == obuf[lastpos+lenf-overlap+i] { s++ } if nbuf[scan-lenb+i] == obuf[pos-lenb+i] { s-- } if s > Ss { Ss = s lens = i + 1 } } lenf += lens - overlap lenb -= lens } for i := 0; i < lenf; i++ { db[dblen+i] = nbuf[lastscan+i] - obuf[lastpos+i] } for i := 0; i < (scan-lenb)-(lastscan+lenf); i++ { eb[eblen+i] = nbuf[lastscan+lenf+i] } dblen += lenf eblen += (scan - lenb) - (lastscan + lenf) err = binary.Write(pfbz2, signMagLittleEndian{}, int64(lenf)) if err != nil { pfbz2.Close() return err } val := (scan - lenb) - (lastscan + lenf) err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val)) if err != nil { pfbz2.Close() return err } val = (pos - lenb) - (lastpos + lenf) err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val)) if err != nil { pfbz2.Close() return err } lastscan = scan - lenb lastpos = pos - lenb lastoffset = pos - scan } } err = pfbz2.Close() if err != nil { return err } // Compute size of compressed ctrl data l64, err := patch.Seek(0, 1) if err != nil { return err } hdr.CtrlLen = int64(l64 - 32) // Write compressed diff data pfbz2, err = newBzip2Writer(patch) if err != nil { return err } n, err := pfbz2.Write(db[:dblen]) if err != nil { pfbz2.Close() return err } if n != dblen { pfbz2.Close() return io.ErrShortWrite } err = pfbz2.Close() if err != nil { return err } // Compute size of compressed diff data n64, err := patch.Seek(0, 1) if err != nil { return err } hdr.DiffLen = n64 - l64 // Write compressed extra data pfbz2, err = newBzip2Writer(patch) if err != nil { return err } n, err = pfbz2.Write(eb[:eblen]) if err != nil { pfbz2.Close() return err } if n != eblen { pfbz2.Close() return io.ErrShortWrite } err = pfbz2.Close() if err != nil { return err } // Seek to the beginning, write the header, and close the file _, err = patch.Seek(0, 0) if err != nil { return err } err = binary.Write(patch, signMagLittleEndian{}, &hdr) if err != nil { return err } return nil } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/diff_test.go000066400000000000000000000024151274405065600227200ustar00rootroot00000000000000package binarydist import ( "bytes" "io/ioutil" "os" "os/exec" "testing" ) var diffT = []struct { old *os.File new *os.File }{ { old: mustWriteRandFile("test.old", 1e3, 1), new: mustWriteRandFile("test.new", 1e3, 2), }, { old: mustOpen("testdata/sample.old"), new: mustOpen("testdata/sample.new"), }, } func TestDiff(t *testing.T) { for _, s := range diffT { got, err := ioutil.TempFile("/tmp", "bspatch.") if err != nil { panic(err) } os.Remove(got.Name()) exp, err := ioutil.TempFile("/tmp", "bspatch.") if err != nil { panic(err) } cmd := exec.Command("bsdiff", s.old.Name(), s.new.Name(), exp.Name()) cmd.Stdout = os.Stdout err = cmd.Run() os.Remove(exp.Name()) if err != nil { panic(err) } err = Diff(s.old, s.new, got) if err != nil { t.Fatal("err", err) } _, err = got.Seek(0, 0) if err != nil { panic(err) } gotBuf := mustReadAll(got) expBuf := mustReadAll(exp) if !bytes.Equal(gotBuf, expBuf) { t.Fail() t.Logf("diff %s %s", s.old.Name(), s.new.Name()) t.Logf("%s: len(got) = %d", got.Name(), len(gotBuf)) t.Logf("%s: len(exp) = %d", exp.Name(), len(expBuf)) i := matchlen(gotBuf, expBuf) t.Logf("produced different output at pos %d; %d != %d", i, gotBuf[i], expBuf[i]) } } } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/doc.go000066400000000000000000000013711274405065600215160ustar00rootroot00000000000000// Package binarydist implements binary diff and patch as described on // http://www.daemonology.net/bsdiff/. It reads and writes files // compatible with the tools there. package binarydist var magic = [8]byte{'B', 'S', 'D', 'I', 'F', 'F', '4', '0'} // File format: // 0 8 "BSDIFF40" // 8 8 X // 16 8 Y // 24 8 sizeof(newfile) // 32 X bzip2(control block) // 32+X Y bzip2(diff block) // 32+X+Y ??? bzip2(extra block) // with control block a set of triples (x,y,z) meaning "add x bytes // from oldfile to x bytes from the diff block; copy y bytes from the // extra block; seek forwards in oldfile by z bytes". type header struct { Magic [8]byte CtrlLen int64 DiffLen int64 NewSize int64 } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/encoding.go000066400000000000000000000023771274405065600225460ustar00rootroot00000000000000package binarydist // SignMagLittleEndian is the numeric encoding used by the bsdiff tools. // It implements binary.ByteOrder using a sign-magnitude format // and little-endian byte order. Only methods Uint64 and String // have been written; the rest panic. type signMagLittleEndian struct{} func (signMagLittleEndian) Uint16(b []byte) uint16 { panic("unimplemented") } func (signMagLittleEndian) PutUint16(b []byte, v uint16) { panic("unimplemented") } func (signMagLittleEndian) Uint32(b []byte) uint32 { panic("unimplemented") } func (signMagLittleEndian) PutUint32(b []byte, v uint32) { panic("unimplemented") } func (signMagLittleEndian) Uint64(b []byte) uint64 { y := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7]&0x7f)<<56 if b[7]&0x80 != 0 { y = -y } return uint64(y) } func (signMagLittleEndian) PutUint64(b []byte, v uint64) { x := int64(v) neg := x < 0 if neg { x = -x } b[0] = byte(x) b[1] = byte(x >> 8) b[2] = byte(x >> 16) b[3] = byte(x >> 24) b[4] = byte(x >> 32) b[5] = byte(x >> 40) b[6] = byte(x >> 48) b[7] = byte(x >> 56) if neg { b[7] |= 0x80 } } func (signMagLittleEndian) String() string { return "signMagLittleEndian" } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/patch.go000066400000000000000000000041271274405065600220520ustar00rootroot00000000000000package binarydist import ( "bytes" "compress/bzip2" "encoding/binary" "errors" "io" "io/ioutil" ) var ErrCorrupt = errors.New("corrupt patch") // Patch applies patch to old, according to the bspatch algorithm, // and writes the result to new. func Patch(old io.Reader, new io.Writer, patch io.Reader) error { var hdr header err := binary.Read(patch, signMagLittleEndian{}, &hdr) if err != nil { return err } if hdr.Magic != magic { return ErrCorrupt } if hdr.CtrlLen < 0 || hdr.DiffLen < 0 || hdr.NewSize < 0 { return ErrCorrupt } ctrlbuf := make([]byte, hdr.CtrlLen) _, err = io.ReadFull(patch, ctrlbuf) if err != nil { return err } cpfbz2 := bzip2.NewReader(bytes.NewReader(ctrlbuf)) diffbuf := make([]byte, hdr.DiffLen) _, err = io.ReadFull(patch, diffbuf) if err != nil { return err } dpfbz2 := bzip2.NewReader(bytes.NewReader(diffbuf)) // The entire rest of the file is the extra block. epfbz2 := bzip2.NewReader(patch) obuf, err := ioutil.ReadAll(old) if err != nil { return err } nbuf := make([]byte, hdr.NewSize) var oldpos, newpos int64 for newpos < hdr.NewSize { var ctrl struct{ Add, Copy, Seek int64 } err = binary.Read(cpfbz2, signMagLittleEndian{}, &ctrl) if err != nil { return err } // Sanity-check if newpos+ctrl.Add > hdr.NewSize { return ErrCorrupt } // Read diff string _, err = io.ReadFull(dpfbz2, nbuf[newpos:newpos+ctrl.Add]) if err != nil { return ErrCorrupt } // Add old data to diff string for i := int64(0); i < ctrl.Add; i++ { if oldpos+i >= 0 && oldpos+i < int64(len(obuf)) { nbuf[newpos+i] += obuf[oldpos+i] } } // Adjust pointers newpos += ctrl.Add oldpos += ctrl.Add // Sanity-check if newpos+ctrl.Copy > hdr.NewSize { return ErrCorrupt } // Read extra string _, err = io.ReadFull(epfbz2, nbuf[newpos:newpos+ctrl.Copy]) if err != nil { return ErrCorrupt } // Adjust pointers newpos += ctrl.Copy oldpos += ctrl.Seek } // Write the new file for len(nbuf) > 0 { n, err := new.Write(nbuf) if err != nil { return err } nbuf = nbuf[n:] } return nil } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/patch_test.go000066400000000000000000000022461274405065600231110ustar00rootroot00000000000000package binarydist import ( "io/ioutil" "os" "os/exec" "testing" ) func TestPatch(t *testing.T) { mustWriteRandFile("test.old", 1e3, 1) mustWriteRandFile("test.new", 1e3, 2) got, err := ioutil.TempFile("/tmp", "bspatch.") if err != nil { panic(err) } os.Remove(got.Name()) err = exec.Command("bsdiff", "test.old", "test.new", "test.patch").Run() if err != nil { panic(err) } err = Patch(mustOpen("test.old"), got, mustOpen("test.patch")) if err != nil { t.Fatal("err", err) } ref, err := got.Seek(0, 2) if err != nil { panic(err) } t.Logf("got %d bytes", ref) if n := fileCmp(got, mustOpen("test.new")); n > -1 { t.Fatalf("produced different output at pos %d", n) } } func TestPatchHk(t *testing.T) { got, err := ioutil.TempFile("/tmp", "bspatch.") if err != nil { panic(err) } os.Remove(got.Name()) err = Patch(mustOpen("testdata/sample.old"), got, mustOpen("testdata/sample.patch")) if err != nil { t.Fatal("err", err) } ref, err := got.Seek(0, 2) if err != nil { panic(err) } t.Logf("got %d bytes", ref) if n := fileCmp(got, mustOpen("testdata/sample.new")); n > -1 { t.Fatalf("produced different output at pos %d", n) } } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/seek.go000066400000000000000000000014061274405065600216770ustar00rootroot00000000000000package binarydist import ( "errors" ) type seekBuffer struct { buf []byte pos int } func (b *seekBuffer) Write(p []byte) (n int, err error) { n = copy(b.buf[b.pos:], p) if n == len(p) { b.pos += n return n, nil } b.buf = append(b.buf, p[n:]...) b.pos += len(p) return len(p), nil } func (b *seekBuffer) Seek(offset int64, whence int) (ret int64, err error) { var abs int64 switch whence { case 0: abs = offset case 1: abs = int64(b.pos) + offset case 2: abs = int64(len(b.buf)) + offset default: return 0, errors.New("binarydist: invalid whence") } if abs < 0 { return 0, errors.New("binarydist: negative position") } if abs >= 1<<31 { return 0, errors.New("binarydist: position out of range") } b.pos = int(abs) return abs, nil } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/sort_test.go000066400000000000000000000007761274405065600230070ustar00rootroot00000000000000package binarydist import ( "bytes" "crypto/rand" "testing" ) var sortT = [][]byte{ mustRandBytes(1000), mustReadAll(mustOpen("test.old")), []byte("abcdefabcdef"), } func TestQsufsort(t *testing.T) { for _, s := range sortT { I := qsufsort(s) for i := 1; i < len(I); i++ { if bytes.Compare(s[I[i-1]:], s[I[i]:]) > 0 { t.Fatalf("unsorted at %d", i) } } } } func mustRandBytes(n int) []byte { b := make([]byte, n) _, err := rand.Read(b) if err != nil { panic(err) } return b } binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/testdata/000077500000000000000000000000001274405065600222315ustar00rootroot00000000000000binarydist-3035450ff8b987ec4373f65c40767f096b35f2d2/testdata/sample.new000066400000000000000000000234201274405065600242260ustar00rootroot00000000000000H__PAGEZERO__TEXT@/@/__text__TEXT __symbol_stub1__TEXT8__DATAP/@ @/Q__data__DATAP/ P@/__nl_symbol_ptr__DATA 0 0__bss__DATA00H__LINKEDIT`2?__DWARF0ul__debug_abbrev__DWARF0__debug_line__DWARF̠0__debug_frame__DWARFEl2__debug_info__DWARFC 4__debug_pubnames__DWARFFM <__debug_pubtypes__DWARFSc>__debug_aranges__DWARF0>__debug_gdb_scri__DWARF1D ?*?!? P?: /usr/lib/dyld 8/usr/lib/libSystem.B.dylib h/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation `/System/Library/Frameworks/Security.framework/Versions/A/Security$ #.eH %H;!wyH$H\$H,%J0H+,%J0kH\$H$H+$kHt$(H|$HHH$H$H+$kH|$ H1HH[QH\$0H\$HH\$8H+Hl$`HkHl$hH\$`H;tpH|$pH1HHH\$pD$XD$\H\$PH$Hl$`H+Hl$hHkHt$H|$PH?HHH$Hl$PH+l$Xkl$\k H\$HHkH,$3H\$H3SH\$H CH\$HH$H\$H3SH\$H C.TH\$HHk0H,$H4%KH|$HHH4%H|$HH0H\$HHk0H,$H4%hH|$HHH4% H|$HH/H\$HH$HĀeH %HD$H;wlHH$H$H+H$Hk dH\$H+Hl$Pkl$Xk l$\H\$ H+Hl$0HkHl$8H\$0H;tpH|$xH1HHH\$xD$hD$lH\$`H$Hl$0H+Hl$8HkHt$H|$`H?HHH$Hl$`H+l$hkl$lk H\$PH\$@\$X\$H\$\\$LHD$pH$蔲HD$Hl$@H(l$Hhl$Lh @@HD$pH$RHD$H\$pH\$8Ht$H|$HHH$H$*H$Hk0H,$H4%H|$HHH4% H|$HHg.HĈeH %H;!wH@Hl$PH,$ǽH\$H kHL$(l$0HD$8H$`/虱HD$Hl$(H(l$0h@@HD$8H$^HD$H\$8H\$DHt$H|$HHH\$HH$9H\$HHk0H,$H4%H|$HHH4%'!H|$HHy-H@eH %H;!wHpH$[H\$H$H+H$HknH\$H HC\$(ڀH$H;H$H$HD$PHD$XH4%\$H|$@HHH\$@H HCH$ pHD$[Ht$@H|$HHHL$ HD$(HL$PHD$XHL$PHD$XH\$xH8H HCH$H;H$H$H$HT$`HHL$hHK<T$T$8}(AuwH$^H\$Hl$`H+Hl$hHkT$8H\$Hي\$ <t>HD$<Yi9|D$<H\$xl$__debug_aranges__DWARF0>__debug_gdb_scri__DWARF1%>*?!? P?: /usr/lib/dyld 8/usr/lib/libSystem.B.dylib h/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation `/System/Library/Frameworks/Security.framework/Versions/A/Security@$ H.eH %H;!w>H$H\$H,%:0H+,%:0kH\$H$H+$kHt$(H|$HHH$H$H+$kH|$ H1HHH\$0H\$HH\$8H+Hl$`HkHl$hH\$`H;tpH|$pH1HHH\$pD$XD$\H\$PH$Hl$`H+Hl$hHkjHt$H|$PH?HHH$Hl$PH+l$Xkl$\k mH\$HHkH,$+H\$H3SH\$H CH\$HH$H\$H3SH\$H CH\$HHk0H,$H4%`BH|$HHH4%yH|$HH胥H\$HHk0H,$H4%PH|$HHH4% H|$HHOH\$HH$HĀeH %HD$H;w1HH$H$H+H$Hkx`H\$H+Hl$Pkl$Xk l$\H\$ H+Hl$0HkHl$8H\$0H;tpH|$xH1HHH\$xD$hD$lH\$`H$Hl$0H+Hl$8HkHt$H|$`H?HHH$Hl$`H+l$hkl$lk .lH\$PH\$@\$X\$H\$\\$LHD$pH$YHD$Hl$@H(l$Hhl$Lh @@HD$pH$IHD$H\$pH\$Ht$H|$HHH$H$*H$Hk0H,$H4%H|$HHH4% H|$HHͣHĈeH %H;!wfH@Hl$PH,$H\$H kHL$(l$0HD$8H$h&^HD$Hl$(H(l$0h@@HD$8H$ UHD$H\$8H\$ Ht$H|$HHH\$HH$9H\$HHk0H,$H4%H|$HHH4%!H|$HHߢH@eH %H;!wHpH$RH\$H$H+H$Hk3H\$H HC\$(ڀH$H;H$H$HD$PHD$XH4%xR$H|$@HHH\$@H HCH$(gHD$RHt$@H|$HHHL$ HD$(HL$PHD$XHL$PHD$XH\$xH8H HCH$H;H$H$H$HT$`HHL$hHKT$T$8}(AuwH$ UH\$Hl$`H+Hl$hHk]T$8H\$Hي\$ <t>HD$<Yi9|D$<H\$xl$Gm Ǭl0Odtn^(@ - D9[82\ @y0yЖ8 /j񵎻ݡt9n^E4νNU4NJS(H$R"?IŹ2Yakb%2OtiwВ0f)#2 씱$غ9S=r܅ħEr-4q d,C^1^snU=Fߖvk9iϸ;MuN-Lo,@zZR&ˢ n<0٧؇L/Qh. E! 9h~T 53BimEzk!|7Mo,Npn!E .?ٶ^VImP%!|*)„UNpBZh91AY&SY+H@ dD`@GLoDB @a@@BH\&5L&4=LMHdPy $z)* aM"$p\zD NI,1X"9 %U"yeO@X׃䬅8}I zplv[9tAҸkV "(H j