// 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] }