aboutsummaryrefslogtreecommitdiff
path: root/objectid
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-04 08:26:56 +0800
committerGravatar Runxi Yu2026-03-04 08:59:53 +0800
commitab7501be34032fb9e5c48726a68ae90a917af9eb (patch)
tree20d005647569befea8133e953c3270e8fd2a2a5b /objectid
parent*: gofumpt (diff)
signatureNo signature
*: Lint
Diffstat (limited to 'objectid')
-rw-r--r--objectid/objectid.go21
-rw-r--r--objectid/objectid_test.go19
2 files changed, 36 insertions, 4 deletions
diff --git a/objectid/objectid.go b/objectid/objectid.go
index 7ce011f3..c1ebfb2c 100644
--- a/objectid/objectid.go
+++ b/objectid/objectid.go
@@ -43,9 +43,11 @@ var algorithmTable = [...]algorithmDetails{
size: sha1.Size,
sum: func(data []byte) ObjectID {
sum := sha1.Sum(data) //#nosec G401
+
var id ObjectID
copy(id.data[:], sum[:])
id.algo = AlgorithmSHA1
+
return id
},
new: sha1.New,
@@ -55,9 +57,11 @@ var algorithmTable = [...]algorithmDetails{
size: sha256.Size,
sum: func(data []byte) ObjectID {
sum := sha256.Sum256(data)
+
var id ObjectID
copy(id.data[:], sum[:])
id.algo = AlgorithmSHA256
+
return id
},
new: sha256.New,
@@ -69,12 +73,13 @@ var (
supportedAlgorithms []Algorithm
)
-func init() {
+func init() { //nolint:gochecknoinits
for algo := Algorithm(0); int(algo) < len(algorithmTable); algo++ {
info := algorithmTable[algo]
if info.name == "" {
continue
}
+
algorithmByName[info.name] = algo
supportedAlgorithms = append(supportedAlgorithms, algo)
}
@@ -89,6 +94,7 @@ func SupportedAlgorithms() []Algorithm {
// ParseAlgorithm parses a canonical algorithm name (e.g. "sha1", "sha256").
func ParseAlgorithm(s string) (Algorithm, bool) {
algo, ok := algorithmByName[s]
+
return algo, ok
}
@@ -103,6 +109,7 @@ func (algo Algorithm) String() string {
if inf.name == "" {
return "unknown"
}
+
return inf.name
}
@@ -122,6 +129,7 @@ func (algo Algorithm) New() (hash.Hash, error) {
if newFn == nil {
return nil, ErrInvalidAlgorithm
}
+
return newFn(), nil
}
@@ -150,12 +158,14 @@ func (id ObjectID) Size() int {
// String returns the canonical hex representation.
func (id ObjectID) String() string {
size := id.Size()
+
return hex.EncodeToString(id.data[:size])
}
// Bytes returns a copy of the object ID bytes.
func (id ObjectID) Bytes() []byte {
size := id.Size()
+
return append([]byte(nil), id.data[:size]...)
}
@@ -167,6 +177,7 @@ func (id ObjectID) Bytes() []byte {
// Use Bytes when an independent copy is required.
func (id *ObjectID) RawBytes() []byte {
size := id.Size()
+
return id.data[:size:size]
}
@@ -176,18 +187,23 @@ func ParseHex(algo Algorithm, s string) (ObjectID, error) {
if algo.Size() == 0 {
return id, ErrInvalidAlgorithm
}
+
if len(s)%2 != 0 {
return id, fmt.Errorf("%w: odd hex length %d", ErrInvalidObjectID, len(s))
}
+
if len(s) != algo.HexLen() {
return id, fmt.Errorf("%w: got %d chars, expected %d", ErrInvalidObjectID, len(s), algo.HexLen())
}
+
decoded, err := hex.DecodeString(s)
if err != nil {
return id, fmt.Errorf("%w: decode: %w", ErrInvalidObjectID, err)
}
+
copy(id.data[:], decoded)
id.algo = algo
+
return id, nil
}
@@ -197,10 +213,13 @@ func FromBytes(algo Algorithm, b []byte) (ObjectID, error) {
if algo.Size() == 0 {
return id, ErrInvalidAlgorithm
}
+
if len(b) != algo.Size() {
return id, fmt.Errorf("%w: got %d bytes, expected %d", ErrInvalidObjectID, len(b), algo.Size())
}
+
copy(id.data[:], b)
id.algo = algo
+
return id, nil
}
diff --git a/objectid/objectid_test.go b/objectid/objectid_test.go
index ef191d39..1c5f337a 100644
--- a/objectid/objectid_test.go
+++ b/objectid/objectid_test.go
@@ -48,13 +48,16 @@ func TestParseHexRoundtrip(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
+
id, err := objectid.ParseHex(tt.algo, tt.hex)
if err != nil {
t.Fatalf("ParseHex failed: %v", err)
}
+
if got := id.String(); got != tt.hex {
t.Fatalf("String() = %q, want %q", got, tt.hex)
}
+
if got := id.Size(); got != tt.algo.Size() {
t.Fatalf("Size() = %d, want %d", got, tt.algo.Size())
}
@@ -68,6 +71,7 @@ func TestParseHexRoundtrip(t *testing.T) {
if err != nil {
t.Fatalf("FromBytes failed: %v", err)
}
+
if id2.String() != tt.hex {
t.Fatalf("FromBytes roundtrip = %q, want %q", id2.String(), tt.hex)
}
@@ -92,7 +96,9 @@ func TestParseHexErrors(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- if _, err := objectid.ParseHex(tt.algo, tt.hex); err == nil {
+
+ _, err := objectid.ParseHex(tt.algo, tt.hex)
+ if err == nil {
t.Fatalf("expected ParseHex error")
}
})
@@ -102,10 +108,13 @@ func TestParseHexErrors(t *testing.T) {
func TestFromBytesErrors(t *testing.T) {
t.Parallel()
- if _, err := objectid.FromBytes(objectid.AlgorithmUnknown, []byte{1, 2}); err == nil {
+ _, err := objectid.FromBytes(objectid.AlgorithmUnknown, []byte{1, 2})
+ if err == nil {
t.Fatalf("expected FromBytes unknown algo error")
}
- if _, err := objectid.FromBytes(objectid.AlgorithmSHA1, []byte{1, 2}); err == nil {
+
+ _, err = objectid.FromBytes(objectid.AlgorithmSHA1, []byte{1, 2})
+ if err == nil {
t.Fatalf("expected FromBytes wrong size error")
}
}
@@ -119,10 +128,12 @@ func TestBytesReturnsCopy(t *testing.T) {
}
b1 := id.Bytes()
+
b2 := id.Bytes()
if !bytes.Equal(b1, b2) {
t.Fatalf("Bytes mismatch")
}
+
b1[0] ^= 0xff
if bytes.Equal(b1, b2) {
t.Fatalf("Bytes should return independent copies")
@@ -141,12 +152,14 @@ func TestRawBytesAliasesStorage(t *testing.T) {
if len(b) != id.Size() {
t.Fatalf("RawBytes len = %d, want %d", len(b), id.Size())
}
+
if cap(b) != len(b) {
t.Fatalf("RawBytes cap = %d, want %d", cap(b), len(b))
}
orig := id.String()
b[0] ^= 0xff
+
if id.String() == orig {
t.Fatalf("RawBytes should alias object ID storage")
}