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  }