package store import ( "crypto/sha256" "encoding/hex" "sort" ) // PrefixSnapshotHash is the content key of prefix_snapshot. // It fingerprints prefix rows only. Peer configuration stays on // config_revision.content_hash so a peer-only change can reuse the snapshot. // The result is 64 lowercase hex characters. Row order does not affect the hash. func PrefixSnapshotHash(rows []PrefixRow) string { cp := append([]PrefixRow(nil), rows...) sortPrefixRows(cp) h := sha256.New() for _, row := range cp { _, _ = h.Write([]byte(row.Prefix)) _, _ = h.Write([]byte{1}) if row.CommunityID == nil { _, _ = h.Write([]byte{0}) } else { _, _ = h.Write([]byte{1}) _, _ = h.Write([]byte(*row.CommunityID)) } _, _ = h.Write([]byte{1}) _, _ = h.Write([]byte(row.Source)) _, _ = h.Write([]byte{0}) } sum := h.Sum(nil) return hex.EncodeToString(sum) } func sortPrefixRows(rows []PrefixRow) { sort.Slice(rows, func(i, j int) bool { if rows[i].Prefix != rows[j].Prefix { return rows[i].Prefix < rows[j].Prefix } ni, vi := communitySortKey(rows[i].CommunityID) nj, vj := communitySortKey(rows[j].CommunityID) if ni != nj { return ni } if vi != vj { return vi < vj } return rows[i].Source < rows[j].Source }) } func communitySortKey(id *string) (isNil bool, val string) { if id == nil { return true, "" } return false, *id } func samePrefixRow(a, b PrefixRow) bool { if a.Prefix != b.Prefix || a.Source != b.Source { return false } if (a.CommunityID == nil) != (b.CommunityID == nil) { return false } if a.CommunityID != nil && *a.CommunityID != *b.CommunityID { return false } return true }