internal: improve use of detrand in descfmt and errors

Use a non-breaking space instead of two spaces to vary the output.
This keeps the mutated version aesthetically similar to the normal one.

Change-Id: Ib4ade2795004fe5b30e454e7e533e5a0e3a9ffa2
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/194157
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/internal/descfmt/stringer.go b/internal/descfmt/stringer.go
index a6d33c0..4cb8d6d 100644
--- a/internal/descfmt/stringer.go
+++ b/internal/descfmt/stringer.go
@@ -43,7 +43,7 @@
 		case pref.Descriptor:
 			name = reflect.ValueOf(vs).MethodByName("Get").Type().Out(0).Name() + "s"
 		}
-		start, end = name+randomSpace()+"{", "}"
+		start, end = name+"{", "}"
 	}
 
 	var ss []string
@@ -123,7 +123,7 @@
 
 	start, end := "{", "}"
 	if isRoot {
-		start = rt.Name() + randomSpace() + "{"
+		start = rt.Name() + "{"
 	}
 
 	_, isFile := t.(pref.FileDescriptor)
@@ -269,7 +269,7 @@
 	// In single line mode, simply join all records with commas.
 	if !rs.allowMulti {
 		for _, r := range rs.recs {
-			ss = append(ss, r[0]+": "+r[1])
+			ss = append(ss, r[0]+formatColon(0)+r[1])
 		}
 		return joinStrings(ss, false)
 	}
@@ -278,15 +278,14 @@
 	var maxLen int
 	flush := func(i int) {
 		for _, r := range rs.recs[len(ss):i] {
-			padding := strings.Repeat(" ", maxLen-len(r[0]))
-			ss = append(ss, r[0]+": "+padding+r[1])
+			ss = append(ss, r[0]+formatColon(maxLen-len(r[0]))+r[1])
 		}
 		maxLen = 0
 	}
 	for i, r := range rs.recs {
 		if isMulti := strings.Contains(r[1], "\n"); isMulti {
 			flush(i)
-			ss = append(ss, r[0]+": "+strings.Join(strings.Split(r[1], "\n"), "\n\t"))
+			ss = append(ss, r[0]+formatColon(0)+strings.Join(strings.Split(r[1], "\n"), "\n\t"))
 		} else if maxLen < len(r[0]) {
 			maxLen = len(r[0])
 		}
@@ -295,6 +294,17 @@
 	return joinStrings(ss, true)
 }
 
+func formatColon(padding int) string {
+	// Deliberately introduce instability into the debug output to
+	// discourage users from performing string comparisons.
+	// This provides us flexibility to change the output in the future.
+	if detrand.Bool() {
+		return ":" + strings.Repeat(" ", 1+padding) // use non-breaking spaces (U+00a0)
+	} else {
+		return ":" + strings.Repeat(" ", 1+padding) // use regular spaces (U+0020)
+	}
+}
+
 func joinStrings(ss []string, isMulti bool) string {
 	if len(ss) == 0 {
 		return ""
@@ -304,12 +314,3 @@
 	}
 	return strings.Join(ss, ", ")
 }
-
-// randomSpace randomly returns a string that is either empty or a single space.
-// This is done deliberately to ensure that the output is slightly non-stable.
-//
-// These makes it harder for people to depend on the debug string as stable
-// and provides us the flexibility to make changes.
-func randomSpace() string {
-	return " "[:detrand.Intn(2)]
-}
diff --git a/internal/detrand/rand.go b/internal/detrand/rand.go
index f5d9eeb..a904dd1 100644
--- a/internal/detrand/rand.go
+++ b/internal/detrand/rand.go
@@ -18,24 +18,16 @@
 // Disable disables detrand such that all functions returns the zero value.
 // This function is not concurrent-safe and must be called during program init.
 func Disable() {
-	binHash = 0
+	randSeed = 0
 }
 
 // Bool returns a deterministically random boolean.
 func Bool() bool {
-	return binHash%2 == 1
+	return randSeed%2 == 1
 }
 
-// Intn returns a deterministically random integer within [0,n).
-func Intn(n int) int {
-	if n <= 0 {
-		panic("invalid argument to Intn")
-	}
-	return int(binHash % uint64(n))
-}
-
-// binHash is a best-effort at an approximate hash of the Go binary.
-var binHash = binaryHash()
+// randSeed is a best-effort at an approximate hash of the Go binary.
+var randSeed = binaryHash()
 
 func binaryHash() uint64 {
 	// Open the Go binary.
diff --git a/internal/errors/errors.go b/internal/errors/errors.go
index db1005d..bc495b4 100644
--- a/internal/errors/errors.go
+++ b/internal/errors/errors.go
@@ -25,10 +25,13 @@
 type prefixError struct{ s string }
 
 var prefix = func() string {
+	// Deliberately introduce instability into the error message string to
+	// discourage users from performing error string comparisons.
 	if detrand.Bool() {
-		return "proto:  "
+		return "proto: " // use non-breaking spaces (U+00a0)
+	} else {
+		return "proto: " // use regular spaces (U+0020)
 	}
-	return "proto: "
 }()
 
 func (e *prefixError) Error() string {