mirror of
https://github.com/golang/go
synced 2024-11-06 00:26:11 -07:00
139 lines
2.9 KiB
Go
139 lines
2.9 KiB
Go
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
// This file provides a compact encoding of
|
||
|
// a map of Mercurial hashes to Git hashes.
|
||
|
|
||
|
package redirect
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"os"
|
||
|
"sort"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// hashMap is a map of Mercurial hashes to Git hashes.
|
||
|
type hashMap struct {
|
||
|
file *os.File
|
||
|
entries int
|
||
|
}
|
||
|
|
||
|
// newHashMap takes a file handle that contains a map of Mercurial to Git
|
||
|
// hashes. The file should be a sequence of pairs of little-endian encoded
|
||
|
// uint32s, representing a hgHash and a gitHash respectively.
|
||
|
// The sequence must be sorted by hgHash.
|
||
|
// The file must remain open for as long as the returned hashMap is used.
|
||
|
func newHashMap(f *os.File) (*hashMap, error) {
|
||
|
fi, err := f.Stat()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &hashMap{file: f, entries: int(fi.Size() / 8)}, nil
|
||
|
}
|
||
|
|
||
|
// Lookup finds an hgHash in the map that matches the given prefix, and returns
|
||
|
// its corresponding gitHash. The prefix must be at least 8 characters long.
|
||
|
func (m *hashMap) Lookup(s string) gitHash {
|
||
|
if m == nil {
|
||
|
return 0
|
||
|
}
|
||
|
hg, err := hgHashFromString(s)
|
||
|
if err != nil {
|
||
|
return 0
|
||
|
}
|
||
|
var git gitHash
|
||
|
b := make([]byte, 8)
|
||
|
sort.Search(m.entries, func(i int) bool {
|
||
|
n, err := m.file.ReadAt(b, int64(i*8))
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
if n != 8 {
|
||
|
panic(io.ErrUnexpectedEOF)
|
||
|
}
|
||
|
v := hgHash(binary.LittleEndian.Uint32(b[:4]))
|
||
|
if v == hg {
|
||
|
git = gitHash(binary.LittleEndian.Uint32(b[4:]))
|
||
|
}
|
||
|
return v >= hg
|
||
|
})
|
||
|
return git
|
||
|
}
|
||
|
|
||
|
// hgHash represents the lower (leftmost) 32 bits of a Mercurial hash.
|
||
|
type hgHash uint32
|
||
|
|
||
|
func (h hgHash) String() string {
|
||
|
return intToHash(int64(h))
|
||
|
}
|
||
|
|
||
|
func hgHashFromString(s string) (hgHash, error) {
|
||
|
if len(s) < 8 {
|
||
|
return 0, fmt.Errorf("string too small: len(s) = %d", len(s))
|
||
|
}
|
||
|
hash := s[:8]
|
||
|
i, err := strconv.ParseInt(hash, 16, 64)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
return hgHash(i), nil
|
||
|
}
|
||
|
|
||
|
// gitHash represents the leftmost 28 bits of a Git hash in its upper 28 bits,
|
||
|
// and it encodes hash's repository in the lower 4 bits.
|
||
|
type gitHash uint32
|
||
|
|
||
|
func (h gitHash) Hash() string {
|
||
|
return intToHash(int64(h))[:7]
|
||
|
}
|
||
|
|
||
|
func (h gitHash) Repo() string {
|
||
|
return repo(h & 0xF).String()
|
||
|
}
|
||
|
|
||
|
func intToHash(i int64) string {
|
||
|
s := strconv.FormatInt(i, 16)
|
||
|
if len(s) < 8 {
|
||
|
s = strings.Repeat("0", 8-len(s)) + s
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// repo represents a Go Git repository.
|
||
|
type repo byte
|
||
|
|
||
|
const (
|
||
|
repoGo repo = iota
|
||
|
repoBlog
|
||
|
repoCrypto
|
||
|
repoExp
|
||
|
repoImage
|
||
|
repoMobile
|
||
|
repoNet
|
||
|
repoSys
|
||
|
repoTalks
|
||
|
repoText
|
||
|
repoTools
|
||
|
)
|
||
|
|
||
|
func (r repo) String() string {
|
||
|
return map[repo]string{
|
||
|
repoGo: "go",
|
||
|
repoBlog: "blog",
|
||
|
repoCrypto: "crypto",
|
||
|
repoExp: "exp",
|
||
|
repoImage: "image",
|
||
|
repoMobile: "mobile",
|
||
|
repoNet: "net",
|
||
|
repoSys: "sys",
|
||
|
repoTalks: "talks",
|
||
|
repoText: "text",
|
||
|
repoTools: "tools",
|
||
|
}[r]
|
||
|
}
|