github.com/v2fly/tools@v0.100.0/godoc/redirect/hash.go (about) 1 // Copyright 2014 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. 4 5 // This file provides a compact encoding of 6 // a map of Mercurial hashes to Git hashes. 7 8 package redirect 9 10 import ( 11 "encoding/binary" 12 "fmt" 13 "io" 14 "os" 15 "sort" 16 "strconv" 17 "strings" 18 ) 19 20 // hashMap is a map of Mercurial hashes to Git hashes. 21 type hashMap struct { 22 file *os.File 23 entries int 24 } 25 26 // newHashMap takes a file handle that contains a map of Mercurial to Git 27 // hashes. The file should be a sequence of pairs of little-endian encoded 28 // uint32s, representing a hgHash and a gitHash respectively. 29 // The sequence must be sorted by hgHash. 30 // The file must remain open for as long as the returned hashMap is used. 31 func newHashMap(f *os.File) (*hashMap, error) { 32 fi, err := f.Stat() 33 if err != nil { 34 return nil, err 35 } 36 return &hashMap{file: f, entries: int(fi.Size() / 8)}, nil 37 } 38 39 // Lookup finds an hgHash in the map that matches the given prefix, and returns 40 // its corresponding gitHash. The prefix must be at least 8 characters long. 41 func (m *hashMap) Lookup(s string) gitHash { 42 if m == nil { 43 return 0 44 } 45 hg, err := hgHashFromString(s) 46 if err != nil { 47 return 0 48 } 49 var git gitHash 50 b := make([]byte, 8) 51 sort.Search(m.entries, func(i int) bool { 52 n, err := m.file.ReadAt(b, int64(i*8)) 53 if err != nil { 54 panic(err) 55 } 56 if n != 8 { 57 panic(io.ErrUnexpectedEOF) 58 } 59 v := hgHash(binary.LittleEndian.Uint32(b[:4])) 60 if v == hg { 61 git = gitHash(binary.LittleEndian.Uint32(b[4:])) 62 } 63 return v >= hg 64 }) 65 return git 66 } 67 68 // hgHash represents the lower (leftmost) 32 bits of a Mercurial hash. 69 type hgHash uint32 70 71 func (h hgHash) String() string { 72 return intToHash(int64(h)) 73 } 74 75 func hgHashFromString(s string) (hgHash, error) { 76 if len(s) < 8 { 77 return 0, fmt.Errorf("string too small: len(s) = %d", len(s)) 78 } 79 hash := s[:8] 80 i, err := strconv.ParseInt(hash, 16, 64) 81 if err != nil { 82 return 0, err 83 } 84 return hgHash(i), nil 85 } 86 87 // gitHash represents the leftmost 28 bits of a Git hash in its upper 28 bits, 88 // and it encodes hash's repository in the lower 4 bits. 89 type gitHash uint32 90 91 func (h gitHash) Hash() string { 92 return intToHash(int64(h))[:7] 93 } 94 95 func (h gitHash) Repo() string { 96 return repo(h & 0xF).String() 97 } 98 99 func intToHash(i int64) string { 100 s := strconv.FormatInt(i, 16) 101 if len(s) < 8 { 102 s = strings.Repeat("0", 8-len(s)) + s 103 } 104 return s 105 } 106 107 // repo represents a Go Git repository. 108 type repo byte 109 110 const ( 111 repoGo repo = iota 112 repoBlog 113 repoCrypto 114 repoExp 115 repoImage 116 repoMobile 117 repoNet 118 repoSys 119 repoTalks 120 repoText 121 repoTools 122 ) 123 124 func (r repo) String() string { 125 return map[repo]string{ 126 repoGo: "go", 127 repoBlog: "blog", 128 repoCrypto: "crypto", 129 repoExp: "exp", 130 repoImage: "image", 131 repoMobile: "mobile", 132 repoNet: "net", 133 repoSys: "sys", 134 repoTalks: "talks", 135 repoText: "text", 136 repoTools: "tools", 137 }[r] 138 }