re PR bootstrap/50047 (Revision 177670 failed to bootstrap)
[official-gcc.git] / libgo / go / patch / git.go
blob6516097260a3956c8554bad8db7ec4a4530a9b7c
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 package patch
7 import (
8 "bytes"
9 "compress/zlib"
10 "crypto/sha1"
11 "encoding/git85"
12 "fmt"
13 "io"
14 "os"
17 func gitSHA1(data []byte) []byte {
18 if len(data) == 0 {
19 // special case: 0 length is all zeros sum
20 return make([]byte, 20)
22 h := sha1.New()
23 fmt.Fprintf(h, "blob %d\x00", len(data))
24 h.Write(data)
25 return h.Sum()
28 // BUG(rsc): The Git binary delta format is not implemented, only Git binary literals.
30 // GitBinaryLiteral represents a Git binary literal diff.
31 type GitBinaryLiteral struct {
32 OldSHA1 []byte // if non-empty, the SHA1 hash of the original
33 New []byte // the new contents
36 // Apply implements the Diff interface's Apply method.
37 func (d *GitBinaryLiteral) Apply(old []byte) ([]byte, os.Error) {
38 if sum := gitSHA1(old); !bytes.HasPrefix(sum, d.OldSHA1) {
39 return nil, ErrPatchFailure
41 return d.New, nil
44 func unhex(c byte) uint8 {
45 switch {
46 case '0' <= c && c <= '9':
47 return c - '0'
48 case 'a' <= c && c <= 'f':
49 return c - 'a' + 10
50 case 'A' <= c && c <= 'F':
51 return c - 'A' + 10
53 return 255
56 func getHex(s []byte) (data []byte, rest []byte) {
57 n := 0
58 for n < len(s) && unhex(s[n]) != 255 {
59 n++
61 n &^= 1 // Only take an even number of hex digits.
62 data = make([]byte, n/2)
63 for i := range data {
64 data[i] = unhex(s[2*i])<<4 | unhex(s[2*i+1])
66 rest = s[n:]
67 return
70 // ParseGitBinary parses raw as a Git binary patch.
71 func ParseGitBinary(raw []byte) (Diff, os.Error) {
72 var oldSHA1, newSHA1 []byte
73 var sawBinary bool
75 for {
76 var first []byte
77 first, raw, _ = getLine(raw, 1)
78 first = bytes.TrimSpace(first)
79 if s, ok := skip(first, "index "); ok {
80 oldSHA1, s = getHex(s)
81 if s, ok = skip(s, ".."); !ok {
82 continue
84 newSHA1, s = getHex(s)
85 continue
87 if _, ok := skip(first, "GIT binary patch"); ok {
88 sawBinary = true
89 continue
91 if n, _, ok := atoi(first, "literal ", 10); ok && sawBinary {
92 data := make([]byte, n)
93 d := git85.NewDecoder(bytes.NewBuffer(raw))
94 z, err := zlib.NewReader(d)
95 if err != nil {
96 return nil, err
98 defer z.Close()
99 if _, err = io.ReadFull(z, data); err != nil {
100 if err == os.EOF {
101 err = io.ErrUnexpectedEOF
103 return nil, err
105 var buf [1]byte
106 m, err := z.Read(buf[0:])
107 if m != 0 || err != os.EOF {
108 return nil, os.NewError("Git binary literal longer than expected")
111 if sum := gitSHA1(data); !bytes.HasPrefix(sum, newSHA1) {
112 return nil, os.NewError("Git binary literal SHA1 mismatch")
114 return &GitBinaryLiteral{oldSHA1, data}, nil
116 if !sawBinary {
117 return nil, os.NewError("unexpected Git patch header: " + string(first))
120 panic("unreachable")