blob: d5a1e3eb67b9f1f28b3aeea90bcbc0c065fe5411 [file] [log] [blame]
// 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]
}