sha1.go 2.67 KB
Newer Older
1
// Copyright (C) 2015-2021  Nexedi SA and Contributors.
Kirill Smelkov's avatar
Kirill Smelkov committed
2 3 4 5 6 7
//                          Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
8 9 10 11 12 13
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
Kirill Smelkov's avatar
Kirill Smelkov committed
14 15 16 17
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
18
// See https://www.nexedi.com/licensing for rationale and options.
Kirill Smelkov's avatar
Kirill Smelkov committed
19 20

package main
21
// Git-backup | Sha1 type to work with SHA1 oids
Kirill Smelkov's avatar
Kirill Smelkov committed
22 23

import (
Kirill Smelkov's avatar
Kirill Smelkov committed
24 25 26
	"bytes"
	"encoding/hex"
	"fmt"
27

Kirill Smelkov's avatar
Kirill Smelkov committed
28
	"lab.nexedi.com/kirr/go123/mem"
29

30
	git "github.com/libgit2/git2go/v31"
Kirill Smelkov's avatar
Kirill Smelkov committed
31 32 33 34 35 36 37 38 39 40 41
)

const SHA1_RAWSIZE = 20

// SHA1 value in raw form
// NOTE zero value of Sha1{} is NULL sha1
// NOTE Sha1 size is 20 bytes. On amd64
//      - string size = 16 bytes
//      - slice  size = 24 bytes
//      -> so it is reasonable to pass Sha1 not by reference
type Sha1 struct {
Kirill Smelkov's avatar
Kirill Smelkov committed
42
	sha1 [SHA1_RAWSIZE]byte
Kirill Smelkov's avatar
Kirill Smelkov committed
43 44 45 46 47 48
}

// fmt.Stringer
var _ fmt.Stringer = Sha1{}

func (sha1 Sha1) String() string {
Kirill Smelkov's avatar
Kirill Smelkov committed
49
	return hex.EncodeToString(sha1.sha1[:])
Kirill Smelkov's avatar
Kirill Smelkov committed
50 51 52
}

func Sha1Parse(sha1str string) (Sha1, error) {
Kirill Smelkov's avatar
Kirill Smelkov committed
53 54 55 56 57 58 59 60 61 62
	sha1 := Sha1{}
	if hex.DecodedLen(len(sha1str)) != SHA1_RAWSIZE {
		return Sha1{}, fmt.Errorf("sha1parse: %q invalid", sha1str)
	}
	_, err := hex.Decode(sha1.sha1[:], mem.Bytes(sha1str))
	if err != nil {
		return Sha1{}, fmt.Errorf("sha1parse: %q invalid: %s", sha1str, err)
	}

	return sha1, nil
Kirill Smelkov's avatar
Kirill Smelkov committed
63 64 65 66 67 68
}

// fmt.Scanner
var _ fmt.Scanner = (*Sha1)(nil)

func (sha1 *Sha1) Scan(s fmt.ScanState, ch rune) error {
Kirill Smelkov's avatar
Kirill Smelkov committed
69 70 71 72 73 74 75 76 77 78 79 80 81
	switch ch {
	case 's', 'v':
	default:
		return fmt.Errorf("Sha1.Scan: invalid verb %q", ch)
	}

	tok, err := s.Token(true, nil)
	if err != nil {
		return err
	}

	*sha1, err = Sha1Parse(mem.String(tok))
	return err
Kirill Smelkov's avatar
Kirill Smelkov committed
82 83 84 85
}

// check whether sha1 is null
func (sha1 *Sha1) IsNull() bool {
Kirill Smelkov's avatar
Kirill Smelkov committed
86
	return *sha1 == Sha1{}
Kirill Smelkov's avatar
Kirill Smelkov committed
87 88 89 90 91 92 93 94
}

// for sorting by Sha1
type BySha1 []Sha1

func (p BySha1) Len() int           { return len(p) }
func (p BySha1) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p BySha1) Less(i, j int) bool { return bytes.Compare(p[i].sha1[:], p[j].sha1[:]) < 0 }
95 96 97

// interoperability with git2go
func (sha1 *Sha1) AsOid() *git.Oid {
Kirill Smelkov's avatar
Kirill Smelkov committed
98
	return (*git.Oid)(&sha1.sha1)
99 100 101
}

func Sha1FromOid(oid *git.Oid) Sha1 {
Kirill Smelkov's avatar
Kirill Smelkov committed
102
	return Sha1{*oid}
103
}